+1
This commit is contained in:
+342
-61
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
@@ -13,6 +14,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const version = "1.0.0"
|
||||
|
||||
type client struct {
|
||||
baseURL string
|
||||
http *http.Client
|
||||
@@ -44,6 +47,7 @@ func (c *client) do(method, path string, payload interface{}) ([]byte, int, erro
|
||||
if payload != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
req.Header.Set("X-WPSK-Client", "cli")
|
||||
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
@@ -65,16 +69,31 @@ func main() {
|
||||
defaultAPI = "http://localhost:8080"
|
||||
}
|
||||
apiURL := flag.String("api", defaultAPI, "API base URL")
|
||||
versionFlag := flag.Bool("version", false, "Print CLI version")
|
||||
usage := func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [--api URL] <command> [args]\n\n", os.Args[0])
|
||||
fmt.Fprintln(os.Stderr, "Commands:")
|
||||
fmt.Fprintln(os.Stderr, " health")
|
||||
fmt.Fprintln(os.Stderr, " pending [summary]")
|
||||
fmt.Fprintln(os.Stderr, " pending [summary|text <draft_post_id>]")
|
||||
fmt.Fprintln(os.Stderr, " optimize <post_id> [language]")
|
||||
fmt.Fprintln(os.Stderr, " status <job_id>")
|
||||
fmt.Fprintln(os.Stderr, " changes <post_id>")
|
||||
fmt.Fprintln(os.Stderr, " approve <draft_post_id>")
|
||||
fmt.Fprintln(os.Stderr, " reject <draft_post_id>")
|
||||
fmt.Fprintln(os.Stderr, " ideas [status]")
|
||||
fmt.Fprintln(os.Stderr, " ideas generate")
|
||||
fmt.Fprintln(os.Stderr, " ideas draft <idea_id>")
|
||||
fmt.Fprintln(os.Stderr, " ideas publish <idea_id>")
|
||||
fmt.Fprintln(os.Stderr, " ideas delete <idea_id>")
|
||||
fmt.Fprintln(os.Stderr, " outreach [status]")
|
||||
fmt.Fprintln(os.Stderr, " outreach generate")
|
||||
fmt.Fprintln(os.Stderr, " outreach delete <id>")
|
||||
fmt.Fprintln(os.Stderr, " newsletter [status]")
|
||||
fmt.Fprintln(os.Stderr, " newsletter generate")
|
||||
fmt.Fprintln(os.Stderr, " newsletter delete <id>")
|
||||
fmt.Fprintln(os.Stderr, " audit [limit]")
|
||||
fmt.Fprintln(os.Stderr, " version")
|
||||
fmt.Fprintln(os.Stderr, " shell")
|
||||
fmt.Fprintln(os.Stderr, " help")
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
fmt.Fprintln(os.Stderr, "Examples:")
|
||||
@@ -86,6 +105,11 @@ func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if *versionFlag {
|
||||
fmt.Println(version)
|
||||
return
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) == 0 {
|
||||
usage()
|
||||
@@ -100,67 +124,17 @@ func main() {
|
||||
}
|
||||
|
||||
c := newClient(*apiURL)
|
||||
if cmd != "version" && cmd != "help" {
|
||||
warnPendingOutreach(c)
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case "health":
|
||||
handleSimpleGet(c, "/api/v1/health")
|
||||
case "pending":
|
||||
if len(args) > 0 && args[0] == "summary" {
|
||||
handlePendingSummary(c)
|
||||
return
|
||||
}
|
||||
handleSimpleGet(c, "/api/v1/pending")
|
||||
case "status":
|
||||
if len(args) < 1 {
|
||||
die("status requires <job_id>")
|
||||
}
|
||||
handleSimpleGet(c, "/api/v1/status/"+args[0])
|
||||
case "changes":
|
||||
if len(args) < 1 {
|
||||
die("changes requires <post_id>")
|
||||
}
|
||||
handleSimpleGet(c, "/api/v1/optimization/"+args[0])
|
||||
case "optimize":
|
||||
if len(args) < 1 {
|
||||
die("optimize requires <post_id>")
|
||||
}
|
||||
postID, err := parseIntArg("post_id", args[0])
|
||||
if err != nil {
|
||||
die(err.Error())
|
||||
}
|
||||
language := "en"
|
||||
if len(args) > 1 {
|
||||
language = args[1]
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/optimize", map[string]interface{}{
|
||||
"post_id": postID,
|
||||
"language": language,
|
||||
})
|
||||
case "approve":
|
||||
if len(args) < 1 {
|
||||
die("approve requires <draft_post_id>")
|
||||
}
|
||||
draftID, err := parseIntArg("draft_post_id", args[0])
|
||||
if err != nil {
|
||||
die(err.Error())
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/apply-draft", map[string]interface{}{
|
||||
"draft_post_id": draftID,
|
||||
})
|
||||
case "reject":
|
||||
if len(args) < 1 {
|
||||
die("reject requires <draft_post_id>")
|
||||
}
|
||||
draftID, err := parseIntArg("draft_post_id", args[0])
|
||||
if err != nil {
|
||||
die(err.Error())
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/reject-draft", map[string]interface{}{
|
||||
"draft_post_id": draftID,
|
||||
})
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown command: %s\n\n", cmd)
|
||||
usage()
|
||||
if cmd == "shell" {
|
||||
runInteractive(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := executeCommand(c, cmd, args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -234,6 +208,273 @@ func handlePendingSummary(c *client) {
|
||||
}
|
||||
}
|
||||
|
||||
func executeCommand(c *client, cmd string, args []string) error {
|
||||
switch cmd {
|
||||
case "health":
|
||||
handleSimpleGet(c, "/api/v1/health")
|
||||
case "version":
|
||||
return handleVersion(c)
|
||||
case "pending":
|
||||
if len(args) > 0 && args[0] == "summary" {
|
||||
handlePendingSummary(c)
|
||||
return nil
|
||||
}
|
||||
if len(args) > 0 && args[0] == "text" {
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("pending text requires <draft_post_id>")
|
||||
}
|
||||
draftID, err := parseIntArg("draft_post_id", args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return handleDraftText(c, draftID)
|
||||
}
|
||||
handleSimpleGet(c, "/api/v1/pending")
|
||||
case "status":
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("status requires <job_id>")
|
||||
}
|
||||
handleSimpleGet(c, "/api/v1/status/"+args[0])
|
||||
case "changes":
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("changes requires <post_id>")
|
||||
}
|
||||
handleSimpleGet(c, "/api/v1/optimization/"+args[0])
|
||||
case "optimize":
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("optimize requires <post_id>")
|
||||
}
|
||||
postID, err := parseIntArg("post_id", args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
language := "en"
|
||||
if len(args) > 1 {
|
||||
language = args[1]
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/optimize", map[string]interface{}{
|
||||
"post_id": postID,
|
||||
"language": language,
|
||||
})
|
||||
case "approve":
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("approve requires <draft_post_id>")
|
||||
}
|
||||
draftID, err := parseIntArg("draft_post_id", args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/apply-draft", map[string]interface{}{
|
||||
"draft_post_id": draftID,
|
||||
})
|
||||
case "reject":
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("reject requires <draft_post_id>")
|
||||
}
|
||||
draftID, err := parseIntArg("draft_post_id", args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/reject-draft", map[string]interface{}{
|
||||
"draft_post_id": draftID,
|
||||
})
|
||||
case "ideas":
|
||||
if len(args) == 0 {
|
||||
handleSimpleGet(c, "/api/v1/ideas")
|
||||
return nil
|
||||
}
|
||||
switch args[0] {
|
||||
case "generate":
|
||||
handleSimplePost(c, "/api/v1/ideas/generate", nil)
|
||||
case "draft":
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("ideas draft requires <idea_id>")
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/ideas/"+args[1]+"/draft", nil)
|
||||
case "publish":
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("ideas publish requires <idea_id>")
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/ideas/"+args[1]+"/publish", nil)
|
||||
case "delete":
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("ideas delete requires <idea_id>")
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/ideas/"+args[1]+"/delete", nil)
|
||||
default:
|
||||
status := args[0]
|
||||
handleSimpleGet(c, "/api/v1/ideas?status="+status)
|
||||
}
|
||||
case "outreach":
|
||||
if len(args) == 0 {
|
||||
handleSimpleGet(c, "/api/v1/outreach")
|
||||
return nil
|
||||
}
|
||||
switch args[0] {
|
||||
case "generate":
|
||||
handleSimplePost(c, "/api/v1/outreach/generate", nil)
|
||||
case "delete":
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("outreach delete requires <id>")
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/outreach/"+args[1]+"/delete", nil)
|
||||
default:
|
||||
status := args[0]
|
||||
handleSimpleGet(c, "/api/v1/outreach?status="+status)
|
||||
}
|
||||
case "newsletter":
|
||||
if len(args) == 0 {
|
||||
handleSimpleGet(c, "/api/v1/newsletter")
|
||||
return nil
|
||||
}
|
||||
switch args[0] {
|
||||
case "generate":
|
||||
handleSimplePost(c, "/api/v1/newsletter/generate", nil)
|
||||
case "delete":
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("newsletter delete requires <id>")
|
||||
}
|
||||
handleSimplePost(c, "/api/v1/newsletter/"+args[1]+"/delete", nil)
|
||||
default:
|
||||
status := args[0]
|
||||
handleSimpleGet(c, "/api/v1/newsletter?status="+status)
|
||||
}
|
||||
case "audit":
|
||||
limit := 100
|
||||
if len(args) > 0 {
|
||||
parsed, err := strconv.Atoi(args[0])
|
||||
if err != nil || parsed <= 0 {
|
||||
return fmt.Errorf("audit limit must be a positive integer")
|
||||
}
|
||||
limit = parsed
|
||||
}
|
||||
handleSimpleGet(c, fmt.Sprintf("/api/v1/audit?limit=%d", limit))
|
||||
default:
|
||||
return fmt.Errorf("unknown command: %s", cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runInteractive(c *client) {
|
||||
fmt.Println("WPSideKick interactive shell. Type 'help' or 'exit'.")
|
||||
warnPendingOutreach(c)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for {
|
||||
fmt.Print("wpsk> ")
|
||||
if !scanner.Scan() {
|
||||
break
|
||||
}
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if line == "exit" || line == "quit" {
|
||||
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")
|
||||
continue
|
||||
}
|
||||
tokens := strings.Fields(line)
|
||||
if len(tokens) == 0 {
|
||||
continue
|
||||
}
|
||||
cmd := tokens[0]
|
||||
args := tokens[1:]
|
||||
if err := executeCommand(c, cmd, args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "interactive error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type draftResponse struct {
|
||||
ID int `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Title string `json:"title"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type healthResponse struct {
|
||||
Status string `json:"status"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type outreachResponse struct {
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
func handleDraftText(c *client, draftID int) error {
|
||||
body, status, err := c.do(http.MethodGet, fmt.Sprintf("/api/v1/drafts/%d", draftID), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status < 200 || status >= 300 {
|
||||
return fmt.Errorf("request failed: status %d: %s", status, string(body))
|
||||
}
|
||||
|
||||
var resp draftResponse
|
||||
if err := json.Unmarshal(body, &resp); err != nil {
|
||||
return fmt.Errorf("failed to parse response")
|
||||
}
|
||||
|
||||
fmt.Printf("Draft %d (%s)\n", resp.ID, resp.Status)
|
||||
fmt.Println(strings.Repeat("=", 60))
|
||||
fmt.Println(resp.Title)
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
if strings.TrimSpace(resp.Excerpt) != "" {
|
||||
fmt.Println(wrapText(stripHTML(resp.Excerpt), 100))
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
}
|
||||
fmt.Println(wrapText(stripHTML(resp.Content), 100))
|
||||
fmt.Println()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleVersion(c *client) error {
|
||||
fmt.Printf("CLI version: %s\n", version)
|
||||
body, status, err := c.do(http.MethodGet, "/api/v1/health", nil)
|
||||
if err != nil {
|
||||
fmt.Printf("Server version: unavailable (%v)\n", err)
|
||||
return nil
|
||||
}
|
||||
if status < 200 || status >= 300 {
|
||||
fmt.Printf("Server version: unavailable (status %d)\n", status)
|
||||
return nil
|
||||
}
|
||||
|
||||
var resp healthResponse
|
||||
if err := json.Unmarshal(body, &resp); err != nil {
|
||||
fmt.Println("Server version: unavailable (invalid response)")
|
||||
return nil
|
||||
}
|
||||
|
||||
if resp.Version == "" {
|
||||
fmt.Println("Server version: unavailable")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Server version: %s\n", resp.Version)
|
||||
return nil
|
||||
}
|
||||
|
||||
func warnPendingOutreach(c *client) {
|
||||
body, status, err := c.do(http.MethodGet, "/api/v1/outreach?status=new", nil)
|
||||
if err != nil || status < 200 || status >= 300 {
|
||||
return
|
||||
}
|
||||
var resp outreachResponse
|
||||
if err := json.Unmarshal(body, &resp); err != nil {
|
||||
return
|
||||
}
|
||||
if resp.Count > 0 {
|
||||
fmt.Fprintf(os.Stderr, "⚠ Pending outreach suggestions: %d (run `wp-sk-cli outreach`)\n", resp.Count)
|
||||
}
|
||||
}
|
||||
|
||||
func parseIntArg(name, value string) (int, error) {
|
||||
parsed, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
@@ -246,3 +487,43 @@ func die(msg string) {
|
||||
fmt.Fprintln(os.Stderr, msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func stripHTML(value string) string {
|
||||
out := strings.Builder{}
|
||||
inTag := false
|
||||
for _, r := range value {
|
||||
switch r {
|
||||
case '<':
|
||||
inTag = true
|
||||
case '>':
|
||||
inTag = false
|
||||
default:
|
||||
if !inTag {
|
||||
out.WriteRune(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(out.String())
|
||||
}
|
||||
|
||||
func wrapText(value string, width int) string {
|
||||
if width <= 0 {
|
||||
return value
|
||||
}
|
||||
words := strings.Fields(value)
|
||||
if len(words) == 0 {
|
||||
return ""
|
||||
}
|
||||
var lines []string
|
||||
line := words[0]
|
||||
for _, word := range words[1:] {
|
||||
if len(line)+1+len(word) > width {
|
||||
lines = append(lines, line)
|
||||
line = word
|
||||
} else {
|
||||
line += " " + word
|
||||
}
|
||||
}
|
||||
lines = append(lines, line)
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
+109
-15
@@ -12,7 +12,9 @@ import (
|
||||
"seo-optimizer/internal/agent"
|
||||
"seo-optimizer/internal/api"
|
||||
"seo-optimizer/internal/config"
|
||||
"seo-optimizer/internal/ideas"
|
||||
"seo-optimizer/internal/logger"
|
||||
"seo-optimizer/internal/newsletter"
|
||||
"seo-optimizer/internal/scheduler"
|
||||
"seo-optimizer/internal/seo"
|
||||
"seo-optimizer/internal/storage"
|
||||
@@ -55,19 +57,39 @@ func main() {
|
||||
"username", cfg.WordPress.Username,
|
||||
)
|
||||
|
||||
// Initialize Claude client
|
||||
claudeClient := agent.NewClaudeClient(
|
||||
cfg.Claude.APIKey,
|
||||
cfg.Claude.Model,
|
||||
cfg.Claude.MaxTokens,
|
||||
cfg.Claude.Temperature,
|
||||
log,
|
||||
)
|
||||
|
||||
log.Info("Claude client initialized",
|
||||
"model", cfg.Claude.Model,
|
||||
"max_tokens", cfg.Claude.MaxTokens,
|
||||
)
|
||||
// Initialize LLM client
|
||||
var llmClient agent.LLMClient
|
||||
switch cfg.LLM.Provider {
|
||||
case "claude":
|
||||
llmClient = agent.NewClaudeClient(
|
||||
cfg.Claude.APIKey,
|
||||
cfg.Claude.Model,
|
||||
cfg.Claude.MaxTokens,
|
||||
cfg.Claude.Temperature,
|
||||
log,
|
||||
)
|
||||
log.Info("Claude client initialized",
|
||||
"model", cfg.Claude.Model,
|
||||
"max_tokens", cfg.Claude.MaxTokens,
|
||||
)
|
||||
case "deepseek":
|
||||
llmClient = agent.NewDeepSeekClient(
|
||||
cfg.DeepSeek.APIKey,
|
||||
cfg.DeepSeek.BaseURL,
|
||||
cfg.DeepSeek.Model,
|
||||
cfg.DeepSeek.MaxTokens,
|
||||
cfg.DeepSeek.Temperature,
|
||||
log,
|
||||
)
|
||||
log.Info("DeepSeek client initialized",
|
||||
"model", cfg.DeepSeek.Model,
|
||||
"max_tokens", cfg.DeepSeek.MaxTokens,
|
||||
"base_url", cfg.DeepSeek.BaseURL,
|
||||
)
|
||||
default:
|
||||
log.Error("Unsupported LLM provider", "provider", cfg.LLM.Provider)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Initialize storage
|
||||
store := storage.NewOptimizationStorage(cfg.Storage.BasePath, log)
|
||||
@@ -77,8 +99,46 @@ func main() {
|
||||
}
|
||||
log.Info("Storage initialized", "base_path", cfg.Storage.BasePath)
|
||||
|
||||
// Initialize audit storage
|
||||
auditStore := storage.NewAuditStorage(cfg.Audit.BasePath, log)
|
||||
if err := auditStore.Initialize(); err != nil {
|
||||
log.Error("Failed to initialize audit storage", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Audit storage initialized", "base_path", cfg.Audit.BasePath)
|
||||
|
||||
// Initialize idea storage
|
||||
ideaStore := storage.NewIdeaStorage(cfg.Ideas.BasePath, log)
|
||||
if err := ideaStore.Initialize(); err != nil {
|
||||
log.Error("Failed to initialize idea storage", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Idea storage initialized", "base_path", cfg.Ideas.BasePath)
|
||||
|
||||
// Initialize outreach storage
|
||||
outreachStore := storage.NewOutreachStorage(cfg.Outreach.BasePath, log)
|
||||
if err := outreachStore.Initialize(); err != nil {
|
||||
log.Error("Failed to initialize outreach storage", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Outreach storage initialized", "base_path", cfg.Outreach.BasePath)
|
||||
|
||||
// Initialize newsletter storage
|
||||
newsStore := storage.NewNewsletterStorage(cfg.Newsletter.BasePath, log)
|
||||
if err := newsStore.Initialize(); err != nil {
|
||||
log.Error("Failed to initialize newsletter storage", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Newsletter storage initialized", "base_path", cfg.Newsletter.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 optimizer
|
||||
optimizer := seo.NewOptimizer(wpClient, claudeClient, store, cfg, log)
|
||||
optimizer := seo.NewOptimizer(wpClient, llmClient, cfg.LLM.Provider, store, cfg, log)
|
||||
log.Info("Optimizer initialized")
|
||||
|
||||
// Context with cancellation for graceful shutdown
|
||||
@@ -88,7 +148,7 @@ func main() {
|
||||
// Start API server if enabled
|
||||
var apiServer *api.Server
|
||||
if cfg.API.Enabled {
|
||||
apiServer = api.NewServer(optimizer, wpClient, store, cfg.API.Port, log)
|
||||
apiServer = api.NewServer(optimizer, wpClient, store, auditStore, ideaService, newsService, version, cfg.API.Port, log)
|
||||
|
||||
// Start server in goroutine
|
||||
go func() {
|
||||
@@ -125,6 +185,30 @@ 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)
|
||||
go func() {
|
||||
if err := ideaSched.Start(ctx); err != nil && err != context.Canceled {
|
||||
log.Error("Idea scheduler failed", "error", err)
|
||||
}
|
||||
}()
|
||||
log.Info("Idea scheduler started", "interval", cfg.Ideas.IntervalHours)
|
||||
}
|
||||
|
||||
// Start newsletter scheduler if enabled
|
||||
var newsSched *scheduler.NewsletterScheduler
|
||||
if cfg.Newsletter.Enabled {
|
||||
newsSched = scheduler.NewNewsletterScheduler(newsService, cfg.Newsletter.IntervalHours, log)
|
||||
go func() {
|
||||
if err := newsSched.Start(ctx); err != nil && err != context.Canceled {
|
||||
log.Error("Newsletter scheduler failed", "error", err)
|
||||
}
|
||||
}()
|
||||
log.Info("Newsletter scheduler started", "interval", cfg.Newsletter.IntervalHours)
|
||||
}
|
||||
|
||||
// Wait for interrupt signal
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
|
||||
@@ -156,5 +240,15 @@ func main() {
|
||||
log.Info("Scheduler stopped")
|
||||
}
|
||||
|
||||
if ideaSched != nil {
|
||||
ideaSched.Stop()
|
||||
log.Info("Idea scheduler stopped")
|
||||
}
|
||||
|
||||
if newsSched != nil {
|
||||
newsSched.Stop()
|
||||
log.Info("Newsletter scheduler stopped")
|
||||
}
|
||||
|
||||
log.Info("SEO Optimizer stopped gracefully")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user