+1
This commit is contained in:
+21
-1
@@ -91,6 +91,8 @@ func main() {
|
||||
fmt.Fprintln(os.Stderr, " newsletter [status]")
|
||||
fmt.Fprintln(os.Stderr, " newsletter generate")
|
||||
fmt.Fprintln(os.Stderr, " newsletter delete <id>")
|
||||
fmt.Fprintln(os.Stderr, " orphans")
|
||||
fmt.Fprintln(os.Stderr, " translate <post_id> [language]")
|
||||
fmt.Fprintln(os.Stderr, " audit [limit]")
|
||||
fmt.Fprintln(os.Stderr, " version")
|
||||
fmt.Fprintln(os.Stderr, " shell")
|
||||
@@ -339,6 +341,24 @@ func executeCommand(c *client, cmd string, args []string) error {
|
||||
status := args[0]
|
||||
handleSimpleGet(c, "/api/v1/newsletter?status="+status)
|
||||
}
|
||||
case "orphans":
|
||||
handleSimpleGet(c, "/api/v1/orphans?source=en&target=es")
|
||||
case "translate":
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("translate requires <post_id>")
|
||||
}
|
||||
postID, err := parseIntArg("post_id", args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetLang := "es"
|
||||
if len(args) > 1 {
|
||||
targetLang = args[1]
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/translate", map[string]interface{}{
|
||||
"post_id": postID,
|
||||
"target_lang": targetLang,
|
||||
})
|
||||
case "audit":
|
||||
limit := 100
|
||||
if len(args) > 0 {
|
||||
@@ -372,7 +392,7 @@ func runInteractive(c *client) {
|
||||
return
|
||||
}
|
||||
if line == "help" {
|
||||
fmt.Println("Commands: health, version, pending [summary|text <draft_id>], optimize <id> [lang], status <job_id>, changes <post_id>, approve <draft_id>, reject <draft_id>, ideas [status|generate|draft <id>|publish <id>|delete <id>], outreach [status|generate|delete <id>], newsletter [status|generate|delete <id>], audit [limit], exit")
|
||||
fmt.Println("Commands: health, version, pending [summary|text <draft_id>], optimize <id> [lang], status <job_id>, changes <post_id>, approve <draft_id>, reject <draft_id>, ideas [status|generate|draft <id>|publish <id>|delete <id>], outreach [status|generate|delete <id>], newsletter [status|generate|delete <id>], orphans, translate <post_id> [lang], audit [limit], exit")
|
||||
continue
|
||||
}
|
||||
tokens := strings.Fields(line)
|
||||
|
||||
+88
-3
@@ -6,18 +6,22 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"seo-optimizer/internal/agent"
|
||||
"seo-optimizer/internal/api"
|
||||
"seo-optimizer/internal/brokenlinks"
|
||||
"seo-optimizer/internal/config"
|
||||
"seo-optimizer/internal/ideas"
|
||||
"seo-optimizer/internal/imageaudit"
|
||||
"seo-optimizer/internal/logger"
|
||||
"seo-optimizer/internal/newsletter"
|
||||
"seo-optimizer/internal/scheduler"
|
||||
"seo-optimizer/internal/seo"
|
||||
"seo-optimizer/internal/storage"
|
||||
"seo-optimizer/internal/translation"
|
||||
"seo-optimizer/internal/wordpress"
|
||||
)
|
||||
|
||||
@@ -91,6 +95,22 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Initialize DeepSeek client for translations (optional)
|
||||
var translationClient *agent.DeepSeekClient
|
||||
if strings.TrimSpace(cfg.DeepSeek.APIKey) != "" {
|
||||
translationClient = agent.NewDeepSeekClient(
|
||||
cfg.DeepSeek.APIKey,
|
||||
cfg.DeepSeek.BaseURL,
|
||||
cfg.DeepSeek.Model,
|
||||
cfg.DeepSeek.MaxTokens,
|
||||
cfg.DeepSeek.Temperature,
|
||||
log,
|
||||
)
|
||||
log.Info("DeepSeek translation client initialized", "model", cfg.DeepSeek.Model)
|
||||
} else {
|
||||
log.Warn("DeepSeek API key missing; translation feature will be unavailable")
|
||||
}
|
||||
|
||||
// Initialize storage
|
||||
store := storage.NewOptimizationStorage(cfg.Storage.BasePath, log)
|
||||
if err := store.Initialize(); err != nil {
|
||||
@@ -131,12 +151,43 @@ func main() {
|
||||
}
|
||||
log.Info("Newsletter storage initialized", "base_path", cfg.Newsletter.BasePath)
|
||||
|
||||
// Initialize broken link storage
|
||||
linkStore := storage.NewBrokenLinkStorage(cfg.LinkChecker.BasePath, log)
|
||||
if err := linkStore.Initialize(); err != nil {
|
||||
log.Error("Failed to initialize broken link storage", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Broken link storage initialized", "base_path", cfg.LinkChecker.BasePath)
|
||||
|
||||
// Initialize image audit storage
|
||||
imageAuditStore := storage.NewImageAuditStorage(cfg.ImageAudit.BasePath, log)
|
||||
if err := imageAuditStore.Initialize(); err != nil {
|
||||
log.Error("Failed to initialize image audit storage", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Image audit storage initialized", "base_path", cfg.ImageAudit.BasePath)
|
||||
|
||||
// Initialize idea service
|
||||
ideaService := ideas.NewService(wpClient, llmClient, nil, ideaStore, cfg.Ideas, outreachStore, cfg.Outreach, cfg.Runtime.DryRun, log)
|
||||
|
||||
// Initialize newsletter service
|
||||
newsService := newsletter.NewService(llmClient, nil, newsStore, cfg.Newsletter, log)
|
||||
|
||||
// Initialize broken link service
|
||||
linkService, err := brokenlinks.NewService(wpClient, linkStore, cfg.LinkChecker, log)
|
||||
if err != nil {
|
||||
log.Error("Failed to initialize broken link service", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
imageAuditService, err := imageaudit.NewService(wpClient, imageAuditStore, cfg.ImageAudit, log)
|
||||
if err != nil {
|
||||
log.Error("Failed to initialize image audit service", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
translationService := translation.NewService(wpClient, translationClient, log)
|
||||
|
||||
// Initialize optimizer
|
||||
optimizer := seo.NewOptimizer(wpClient, llmClient, cfg.LLM.Provider, store, cfg, log)
|
||||
log.Info("Optimizer initialized")
|
||||
@@ -148,7 +199,7 @@ func main() {
|
||||
// Start API server if enabled
|
||||
var apiServer *api.Server
|
||||
if cfg.API.Enabled {
|
||||
apiServer = api.NewServer(optimizer, wpClient, store, auditStore, ideaService, newsService, version, cfg.API.Port, log)
|
||||
apiServer = api.NewServer(optimizer, wpClient, store, auditStore, ideaService, newsService, linkService, imageAuditService, translationService, version, cfg.API.Port, log)
|
||||
|
||||
// Start server in goroutine
|
||||
go func() {
|
||||
@@ -188,7 +239,7 @@ func main() {
|
||||
// Start idea scheduler if enabled
|
||||
var ideaSched *scheduler.IdeaScheduler
|
||||
if cfg.Ideas.Enabled || cfg.Outreach.Enabled {
|
||||
ideaSched = scheduler.NewIdeaScheduler(ideaService, cfg.Ideas.IntervalHours, cfg.Ideas.Enabled, cfg.Outreach.Enabled, log)
|
||||
ideaSched = scheduler.NewIdeaScheduler(ideaService, cfg.Ideas.IntervalHours, cfg.Ideas.Enabled, cfg.Outreach.Enabled, auditStore, log)
|
||||
go func() {
|
||||
if err := ideaSched.Start(ctx); err != nil && err != context.Canceled {
|
||||
log.Error("Idea scheduler failed", "error", err)
|
||||
@@ -200,7 +251,7 @@ func main() {
|
||||
// Start newsletter scheduler if enabled
|
||||
var newsSched *scheduler.NewsletterScheduler
|
||||
if cfg.Newsletter.Enabled {
|
||||
newsSched = scheduler.NewNewsletterScheduler(newsService, cfg.Newsletter.IntervalHours, log)
|
||||
newsSched = scheduler.NewNewsletterScheduler(newsService, cfg.Newsletter.IntervalHours, auditStore, log)
|
||||
go func() {
|
||||
if err := newsSched.Start(ctx); err != nil && err != context.Canceled {
|
||||
log.Error("Newsletter scheduler failed", "error", err)
|
||||
@@ -209,6 +260,30 @@ func main() {
|
||||
log.Info("Newsletter scheduler started", "interval", cfg.Newsletter.IntervalHours)
|
||||
}
|
||||
|
||||
// Start broken link scheduler if enabled
|
||||
var linkSched *scheduler.BrokenLinkScheduler
|
||||
if cfg.LinkChecker.Enabled {
|
||||
linkSched = scheduler.NewBrokenLinkScheduler(linkService, cfg.LinkChecker.IntervalHours, auditStore, log)
|
||||
go func() {
|
||||
if err := linkSched.Start(ctx); err != nil && err != context.Canceled {
|
||||
log.Error("Broken link scheduler failed", "error", err)
|
||||
}
|
||||
}()
|
||||
log.Info("Broken link scheduler started", "interval", cfg.LinkChecker.IntervalHours)
|
||||
}
|
||||
|
||||
// Start image audit scheduler if enabled
|
||||
var imageAuditSched *scheduler.ImageAuditScheduler
|
||||
if cfg.ImageAudit.Enabled {
|
||||
imageAuditSched = scheduler.NewImageAuditScheduler(imageAuditService, cfg.ImageAudit.IntervalHours, auditStore, log)
|
||||
go func() {
|
||||
if err := imageAuditSched.Start(ctx); err != nil && err != context.Canceled {
|
||||
log.Error("Image audit scheduler failed", "error", err)
|
||||
}
|
||||
}()
|
||||
log.Info("Image audit scheduler started", "interval", cfg.ImageAudit.IntervalHours)
|
||||
}
|
||||
|
||||
// Wait for interrupt signal
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
|
||||
@@ -250,5 +325,15 @@ func main() {
|
||||
log.Info("Newsletter scheduler stopped")
|
||||
}
|
||||
|
||||
if linkSched != nil {
|
||||
linkSched.Stop()
|
||||
log.Info("Broken link scheduler stopped")
|
||||
}
|
||||
|
||||
if imageAuditSched != nil {
|
||||
imageAuditSched.Stop()
|
||||
log.Info("Image audit scheduler stopped")
|
||||
}
|
||||
|
||||
log.Info("SEO Optimizer stopped gracefully")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user