Initial commit: Open sourcing all of the Maple Open Technologies code.

This commit is contained in:
Bartlomiej Mika 2025-12-02 14:33:08 -05:00
commit 755d54a99d
2010 changed files with 448675 additions and 0 deletions

View file

@ -0,0 +1,102 @@
package site
import (
"fmt"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/validation"
)
// CreateRequest represents the HTTP request for creating a site
// Note: Domain will be extracted from SiteURL by the backend
type CreateRequest struct {
SiteURL string `json:"site_url"`
}
// ValidationErrors represents validation errors in RFC 9457 format
type ValidationErrors struct {
Errors map[string][]string
}
// Error implements the error interface
func (v *ValidationErrors) Error() string {
if len(v.Errors) == 0 {
return ""
}
// For backward compatibility with error logging, format as string
var messages []string
for field, errs := range v.Errors {
for _, err := range errs {
messages = append(messages, fmt.Sprintf("%s: %s", field, err))
}
}
return fmt.Sprintf("validation errors: %v", messages)
}
// Validate validates the create site request fields
// Returns all validation errors grouped together in RFC 9457 format
func (r *CreateRequest) Validate() error {
v := validation.NewValidator()
validationErrors := make(map[string][]string)
// Validate site URL (required)
if err := v.ValidateURL(r.SiteURL, "site_url"); err != nil {
errMsg := extractErrorMessage(err.Error())
validationErrors["site_url"] = append(validationErrors["site_url"], errMsg)
}
// Return all errors grouped together in RFC 9457 format
if len(validationErrors) > 0 {
return &ValidationErrors{Errors: validationErrors}
}
return nil
}
// extractErrorMessage extracts the error message after the field name prefix
// Example: "domain: invalid domain format" -> "Invalid domain format"
func extractErrorMessage(fullError string) string {
// Find the colon separator
colonIndex := -1
for i, char := range fullError {
if char == ':' {
colonIndex = i
break
}
}
if colonIndex == -1 {
// No colon found, capitalize first letter and return
if len(fullError) > 0 {
return string(fullError[0]-32) + fullError[1:]
}
return fullError
}
// Extract message after colon and trim spaces
message := fullError[colonIndex+1:]
if len(message) > 0 && message[0] == ' ' {
message = message[1:]
}
// Capitalize first letter
if len(message) > 0 {
firstChar := message[0]
if firstChar >= 'a' && firstChar <= 'z' {
message = string(firstChar-32) + message[1:]
}
}
return message
}
// CreateResponse represents the HTTP response after creating a site
type CreateResponse struct {
ID string `json:"id"`
Domain string `json:"domain"`
SiteURL string `json:"site_url"`
APIKey string `json:"api_key"` // Only returned once at creation
Status string `json:"status"`
VerificationToken string `json:"verification_token"`
SearchIndexName string `json:"search_index_name"`
VerificationInstructions string `json:"verification_instructions"` // DNS TXT record setup instructions
}

View file

@ -0,0 +1,28 @@
package site
import "time"
// GetResponse represents the HTTP response for getting a site
type GetResponse struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
Domain string `json:"domain"`
SiteURL string `json:"site_url"`
APIKeyPrefix string `json:"api_key_prefix"`
APIKeyLastFour string `json:"api_key_last_four"`
Status string `json:"status"`
IsVerified bool `json:"is_verified"`
SearchIndexName string `json:"search_index_name"`
TotalPagesIndexed int64 `json:"total_pages_indexed"`
LastIndexedAt time.Time `json:"last_indexed_at,omitempty"`
PluginVersion string `json:"plugin_version,omitempty"`
StorageUsedBytes int64 `json:"storage_used_bytes"`
SearchRequestsCount int64 `json:"search_requests_count"`
MonthlyPagesIndexed int64 `json:"monthly_pages_indexed"`
LastResetAt time.Time `json:"last_reset_at"`
Language string `json:"language,omitempty"`
Timezone string `json:"timezone,omitempty"`
Notes string `json:"notes,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

View file

@ -0,0 +1,19 @@
package site
import "time"
// ListResponse represents the HTTP response for listing sites
type ListResponse struct {
Sites []SiteListItem `json:"sites"`
Total int `json:"total"`
}
// SiteListItem represents a site in the list
type SiteListItem struct {
ID string `json:"id"`
Domain string `json:"domain"`
Status string `json:"status"`
IsVerified bool `json:"is_verified"`
TotalPagesIndexed int64 `json:"total_pages_indexed"`
CreatedAt time.Time `json:"created_at"`
}

View file

@ -0,0 +1,10 @@
package site
import "time"
// RotateAPIKeyResponse represents the HTTP response after rotating an API key
type RotateAPIKeyResponse struct {
NewAPIKey string `json:"new_api_key"` // New API key (only returned once)
OldKeyLastFour string `json:"old_key_last_four"`
RotatedAt time.Time `json:"rotated_at"`
}