feat(pages): add AI-powered landing page content generator
Enable automatic landing page content generation using AI: - Generate hero, features, stats, and CTAs from README - Blog section configuration in settings UI - Extract repository metadata for AI context - Merge generated content while preserving existing settings - User-friendly generation button in settings panel
This commit is contained in:
@@ -9,7 +9,10 @@ import (
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitcaddy.com/server/v3/models/repo"
|
||||
"code.gitcaddy.com/server/v3/modules/ai"
|
||||
"code.gitcaddy.com/server/v3/modules/git"
|
||||
"code.gitcaddy.com/server/v3/modules/json"
|
||||
"code.gitcaddy.com/server/v3/modules/log"
|
||||
pages_module "code.gitcaddy.com/server/v3/modules/pages"
|
||||
"code.gitcaddy.com/server/v3/modules/templates"
|
||||
"code.gitcaddy.com/server/v3/services/context"
|
||||
@@ -84,6 +87,7 @@ func setCommonPagesData(ctx *context.Context) {
|
||||
ctx.Data["PagesURL"] = pages_service.GetPagesURL(ctx.Repo.Repository)
|
||||
ctx.Data["PagesTemplates"] = pages_module.ValidTemplates()
|
||||
ctx.Data["PagesTemplateNames"] = pages_module.TemplateDisplayNames()
|
||||
ctx.Data["AIEnabled"] = ai.IsEnabled()
|
||||
}
|
||||
|
||||
// Pages shows the repository pages settings (General page)
|
||||
@@ -193,6 +197,30 @@ func PagesPost(ctx *context.Context) {
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.domain_verified"))
|
||||
}
|
||||
case "ai_generate":
|
||||
readme := loadRawReadme(ctx, ctx.Repo.Repository)
|
||||
generated, err := pages_service.GenerateLandingPageContent(ctx, ctx.Repo.Repository, readme)
|
||||
if err != nil {
|
||||
log.Error("AI landing page generation failed: %v", err)
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.ai_generate_failed"))
|
||||
ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/pages")
|
||||
return
|
||||
}
|
||||
// Merge AI-generated content into existing config, preserving settings
|
||||
config := getPagesLandingConfig(ctx)
|
||||
config.Brand.Name = generated.Brand.Name
|
||||
config.Brand.Tagline = generated.Brand.Tagline
|
||||
config.Hero = generated.Hero
|
||||
config.Stats = generated.Stats
|
||||
config.ValueProps = generated.ValueProps
|
||||
config.Features = generated.Features
|
||||
config.CTASection = generated.CTASection
|
||||
config.SEO = generated.SEO
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.ai_generate_success"))
|
||||
default:
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
@@ -267,6 +295,12 @@ func PagesContentPost(ctx *context.Context) {
|
||||
config.Navigation.ShowRepository = ctx.FormBool("nav_show_repository")
|
||||
config.Navigation.ShowReleases = ctx.FormBool("nav_show_releases")
|
||||
config.Navigation.ShowIssues = ctx.FormBool("nav_show_issues")
|
||||
config.Blog.Enabled = ctx.FormBool("blog_enabled")
|
||||
config.Blog.Headline = ctx.FormString("blog_headline")
|
||||
config.Blog.Subheadline = ctx.FormString("blog_subheadline")
|
||||
if maxPosts := ctx.FormInt("blog_max_posts"); maxPosts > 0 {
|
||||
config.Blog.MaxPosts = maxPosts
|
||||
}
|
||||
config.Stats = nil
|
||||
for i := range 10 {
|
||||
value := ctx.FormString(fmt.Sprintf("stat_value_%d", i))
|
||||
@@ -454,3 +488,38 @@ func PagesThemePost(ctx *context.Context) {
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.saved"))
|
||||
ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/pages/theme")
|
||||
}
|
||||
|
||||
// loadRawReadme loads the raw README content from the repository for AI consumption
|
||||
func loadRawReadme(ctx *context.Context, repo *repo_model.Repository) string {
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
branch := repo.DefaultBranch
|
||||
if branch == "" {
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
commit, err := gitRepo.GetBranchCommit(branch)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, name := range []string{"README.md", "readme.md", "README", "README.txt"} {
|
||||
entry, err := commit.GetTreeEntryByPath(name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
reader, err := entry.Blob().DataAsync()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
content := make([]byte, entry.Blob().Size())
|
||||
_, _ = reader.Read(content)
|
||||
reader.Close()
|
||||
return string(content)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user