Files
WPSidekick/cmd/optimizer/main.go
T
2026-01-25 08:57:55 +01:00

161 lines
3.7 KiB
Go

package main
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"seo-optimizer/internal/agent"
"seo-optimizer/internal/api"
"seo-optimizer/internal/config"
"seo-optimizer/internal/logger"
"seo-optimizer/internal/scheduler"
"seo-optimizer/internal/seo"
"seo-optimizer/internal/storage"
"seo-optimizer/internal/wordpress"
)
const version = "1.0.0"
func main() {
// Load configuration
configPath := os.Getenv("CONFIG_PATH")
if configPath == "" {
configPath = "configs/config.yaml"
}
cfg, err := config.LoadConfig(configPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
os.Exit(1)
}
// Initialize logger
log := logger.NewLogger(cfg.Logging.Level, cfg.Logging.Format)
log.Info("SEO Optimizer starting",
"version", version,
"config", configPath,
)
// Initialize WordPress client
wpClient := wordpress.NewClient(
cfg.WordPress.BaseURL,
cfg.WordPress.Username,
cfg.WordPress.AppPassword,
cfg.WordPress.Timeout,
log,
)
log.Info("WordPress client initialized",
"base_url", cfg.WordPress.BaseURL,
"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 storage
store := storage.NewOptimizationStorage(cfg.Storage.BasePath, log)
if err := store.Initialize(); err != nil {
log.Error("Failed to initialize storage", "error", err)
os.Exit(1)
}
log.Info("Storage initialized", "base_path", cfg.Storage.BasePath)
// Initialize optimizer
optimizer := seo.NewOptimizer(wpClient, claudeClient, store, cfg, log)
log.Info("Optimizer initialized")
// Context with cancellation for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Start API server if enabled
var apiServer *api.Server
if cfg.API.Enabled {
apiServer = api.NewServer(optimizer, wpClient, store, cfg.API.Port, log)
// Start server in goroutine
go func() {
if err := apiServer.Start(); err != nil && err != http.ErrServerClosed {
log.Error("API server failed", "error", err)
}
}()
// Start periodic job cleanup
go apiServer.StartPeriodicCleanup(ctx, 1*time.Hour, 24*time.Hour)
log.Info("API server started", "port", cfg.API.Port)
}
// Start scheduler if enabled
var sched *scheduler.Scheduler
if cfg.Scheduler.Enabled {
sched = scheduler.NewScheduler(
optimizer,
cfg.Scheduler.IntervalHours,
cfg.Scheduler.Languages,
log,
)
go func() {
if err := sched.Start(ctx); err != nil && err != context.Canceled {
log.Error("Scheduler failed", "error", err)
}
}()
log.Info("Scheduler started",
"interval", fmt.Sprintf("%.1fh", cfg.Scheduler.IntervalHours),
"languages", cfg.Scheduler.Languages,
)
}
// Wait for interrupt signal
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
sig := <-sigCh
log.Info("Shutdown signal received", "signal", sig.String())
// Graceful shutdown
log.Info("Initiating graceful shutdown...")
// Cancel context to stop scheduler
cancel()
// Shutdown API server if running
if apiServer != nil {
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer shutdownCancel()
if err := apiServer.Shutdown(shutdownCtx); err != nil {
log.Error("API server shutdown error", "error", err)
} else {
log.Info("API server shut down successfully")
}
}
// Stop scheduler
if sched != nil {
sched.Stop()
log.Info("Scheduler stopped")
}
log.Info("SEO Optimizer stopped gracefully")
}