helpers.go
package assist
import (
"encoding/json"
"strings"
"github.com/readysite/readysite/website/models"
)
// jsonResult marshals a value to JSON string.
func jsonResult(v any) (string, error) {
b, err := json.Marshal(v)
if err != nil {
return "", err
}
return string(b), nil
}
// clonePage creates a deep copy of a page for mutation tracking.
func clonePage(p *models.Page) *models.Page {
return &models.Page{
ParentID: p.ParentID,
Position: p.Position,
}
}
// cloneCollection creates a deep copy of a collection for mutation tracking.
func cloneCollection(c *models.Collection) *models.Collection {
return &models.Collection{
Name: c.Name,
Description: c.Description,
Schema: c.Schema,
ListRule: c.ListRule,
ViewRule: c.ViewRule,
CreateRule: c.CreateRule,
UpdateRule: c.UpdateRule,
DeleteRule: c.DeleteRule,
System: c.System,
}
}
// cloneDocument creates a deep copy of a document for mutation tracking.
func cloneDocument(d *models.Document) *models.Document {
return &models.Document{
CollectionID: d.CollectionID,
Data: d.Data,
}
}
// clonePartial creates a deep copy of a partial for mutation tracking.
func clonePartial(p *models.Partial) *models.Partial {
return &models.Partial{
Name: p.Name,
Description: p.Description,
HTML: p.HTML,
Published: p.Published,
}
}
// cloneFile creates a deep copy of a file for mutation tracking.
func cloneFile(f *models.File) *models.File {
return &models.File{
Name: f.Name,
MimeType: f.MimeType,
Size: f.Size,
UserID: f.UserID,
Published: f.Published,
Path: f.Path,
}
}
// unescapeHTML fixes double-escaped JSON sequences in HTML content.
// This handles cases where the AI sends back content with literal \n and \"
// instead of actual newlines and quotes.
func unescapeHTML(s string) string {
// Only unescape if we detect the pattern of double-escaped content
// (contains literal \n or \" sequences)
if !strings.Contains(s, `\n`) && !strings.Contains(s, `\"`) {
return s
}
// Replace common JSON escape sequences with their actual characters
s = strings.ReplaceAll(s, `\n`, "\n")
s = strings.ReplaceAll(s, `\r`, "\r")
s = strings.ReplaceAll(s, `\t`, "\t")
s = strings.ReplaceAll(s, `\"`, `"`)
s = strings.ReplaceAll(s, `\\`, `\`)
return s
}
// slugify converts a string to a URL-friendly slug.
func slugify(s string) string {
s = strings.ToLower(s)
s = strings.TrimSpace(s)
// Replace common separators with hyphens
for _, sep := range []string{" ", "_", ".", ","} {
s = strings.ReplaceAll(s, sep, "-")
}
// Remove non-alphanumeric characters except hyphens
var result strings.Builder
for _, r := range s {
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
result.WriteRune(r)
}
}
s = result.String()
// Collapse multiple hyphens
for strings.Contains(s, "--") {
s = strings.ReplaceAll(s, "--", "-")
}
// Trim leading/trailing hyphens
s = strings.Trim(s, "-")
return s
}