fix
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
package aiprompts
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
reLegacyHTMLBlock = regexp.MustCompile(`(?is)<\s*(h[1-4]|p|ul|ol|li|b|strong)\s*>\s*\{?\s*(.*?)\s*\}?\s*<\s*/\s*(?:h[1-4]|p|ul|ol|li|b|strong)\s*>`)
|
||||
reLegacyBoldUL = regexp.MustCompile(`(?is)<\s*b\s*>\s*\{?\s*(.*?)\s*\}?\s*<\s*/\s*b\s*>\s*<\s*ul\s*>\s*<\s*li\s*>\s*\{?\s*(.*?)\s*\}?\s*<\s*/\s*li\s*>\s*<\s*/\s*ul\s*>`)
|
||||
)
|
||||
|
||||
// DescriptionFormulaSection is one ordered block in categories.description_template.
|
||||
type DescriptionFormulaSection struct {
|
||||
Type string `json:"type"`
|
||||
Instructions string `json:"instructions"`
|
||||
}
|
||||
|
||||
// DescriptionFormula is the JSON shape stored in categories.description_template
|
||||
// (sections + optional SEO meta instruction strings).
|
||||
type DescriptionFormula struct {
|
||||
Sections []DescriptionFormulaSection `json:"sections,omitempty"`
|
||||
MetaTitle string `json:"metaTitle,omitempty"`
|
||||
MetaDescription string `json:"metaDescription,omitempty"`
|
||||
}
|
||||
|
||||
// DescriptionTemplateNeedsRepair is true when template is missing or has no sections
|
||||
// (and no usable meta instructions).
|
||||
func DescriptionTemplateNeedsRepair(template any) bool {
|
||||
f, ok := parseDescriptionFormula(template)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if len(f.Sections) > 0 {
|
||||
return false
|
||||
}
|
||||
return strings.TrimSpace(f.MetaTitle) == "" && strings.TrimSpace(f.MetaDescription) == ""
|
||||
}
|
||||
|
||||
// DeriveDescriptionFormulaFromLegacyParts builds description_template from split
|
||||
// legacy DescriptionRules HTML + MetaRules.
|
||||
func DeriveDescriptionFormulaFromLegacyParts(parts LegacyEnhanceParts) DescriptionFormula {
|
||||
out := DescriptionFormula{
|
||||
MetaDescription: strings.TrimSpace(parts.MetaRules),
|
||||
}
|
||||
body := strings.TrimSpace(parts.DescriptionRules)
|
||||
if body == "" {
|
||||
return out
|
||||
}
|
||||
|
||||
// Walk left-to-right so section order matches the legacy HTML template.
|
||||
remaining := body
|
||||
for remaining != "" {
|
||||
boldUL := reLegacyBoldUL.FindStringSubmatchIndex(remaining)
|
||||
block := reLegacyHTMLBlock.FindStringSubmatchIndex(remaining)
|
||||
switch {
|
||||
case boldUL != nil && (block == nil || boldUL[0] <= block[0]):
|
||||
m := reLegacyBoldUL.FindStringSubmatch(remaining[boldUL[0]:boldUL[1]])
|
||||
heading, instr := "", ""
|
||||
if len(m) >= 3 {
|
||||
heading = cleanLegacyInstruction(m[1])
|
||||
instr = cleanLegacyInstruction(m[2])
|
||||
}
|
||||
if heading != "" && instr != "" && !strings.Contains(strings.ToLower(instr), strings.ToLower(heading)) {
|
||||
instr = heading + ": " + instr
|
||||
} else if instr == "" {
|
||||
instr = heading
|
||||
}
|
||||
if strings.TrimSpace(instr) != "" {
|
||||
out.Sections = append(out.Sections, DescriptionFormulaSection{Type: "ul", Instructions: instr})
|
||||
}
|
||||
remaining = remaining[boldUL[1]:]
|
||||
case block != nil:
|
||||
m := reLegacyHTMLBlock.FindStringSubmatch(remaining[block[0]:block[1]])
|
||||
if len(m) >= 3 {
|
||||
typ := strings.ToLower(strings.TrimSpace(m[1]))
|
||||
instr := cleanLegacyInstruction(m[2])
|
||||
if instr != "" {
|
||||
switch typ {
|
||||
case "b", "strong":
|
||||
typ = "h2"
|
||||
case "li", "ol":
|
||||
typ = "ul"
|
||||
}
|
||||
out.Sections = append(out.Sections, DescriptionFormulaSection{Type: typ, Instructions: instr})
|
||||
}
|
||||
}
|
||||
remaining = remaining[block[1]:]
|
||||
default:
|
||||
return out
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DeriveDescriptionTemplateJSON encodes DeriveDescriptionFormulaFromLegacyParts for DB write.
|
||||
func DeriveDescriptionTemplateJSON(parts LegacyEnhanceParts) string {
|
||||
f := DeriveDescriptionFormulaFromLegacyParts(parts)
|
||||
if len(f.Sections) == 0 && strings.TrimSpace(f.MetaTitle) == "" && strings.TrimSpace(f.MetaDescription) == "" {
|
||||
return ""
|
||||
}
|
||||
b, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func parseDescriptionFormula(template any) (DescriptionFormula, bool) {
|
||||
if template == nil {
|
||||
return DescriptionFormula{}, false
|
||||
}
|
||||
switch t := template.(type) {
|
||||
case DescriptionFormula:
|
||||
return t, true
|
||||
case *DescriptionFormula:
|
||||
if t == nil {
|
||||
return DescriptionFormula{}, false
|
||||
}
|
||||
return *t, true
|
||||
case []byte:
|
||||
if len(t) == 0 {
|
||||
return DescriptionFormula{}, false
|
||||
}
|
||||
var f DescriptionFormula
|
||||
if err := json.Unmarshal(t, &f); err != nil {
|
||||
return DescriptionFormula{}, false
|
||||
}
|
||||
return f, true
|
||||
case string:
|
||||
s := strings.TrimSpace(t)
|
||||
if s == "" || s == "null" || s == "{}" {
|
||||
return DescriptionFormula{}, false
|
||||
}
|
||||
var f DescriptionFormula
|
||||
if err := json.Unmarshal([]byte(s), &f); err != nil {
|
||||
return DescriptionFormula{}, false
|
||||
}
|
||||
return f, true
|
||||
default:
|
||||
b, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return DescriptionFormula{}, false
|
||||
}
|
||||
var f DescriptionFormula
|
||||
if err := json.Unmarshal(b, &f); err != nil {
|
||||
return DescriptionFormula{}, false
|
||||
}
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user