Refactor main.go
This commit is contained in:
parent
6a4909c1a7
commit
924d80bdca
5 changed files with 325 additions and 118 deletions
|
|
@ -24,17 +24,11 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"git.happydns.org/happyDeliver/internal/app"
|
||||||
|
|
||||||
"git.happydns.org/happyDeliver/internal/api"
|
|
||||||
"git.happydns.org/happyDeliver/internal/config"
|
"git.happydns.org/happyDeliver/internal/config"
|
||||||
"git.happydns.org/happyDeliver/internal/receiver"
|
|
||||||
"git.happydns.org/happyDeliver/internal/storage"
|
|
||||||
"git.happydns.org/happyDeliver/web"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "0.1.0-dev"
|
const version = "0.1.0-dev"
|
||||||
|
|
@ -52,9 +46,13 @@ func main() {
|
||||||
|
|
||||||
switch command {
|
switch command {
|
||||||
case "server":
|
case "server":
|
||||||
runServer(cfg)
|
if err := app.RunServer(cfg); err != nil {
|
||||||
|
log.Fatalf("Server error: %v", err)
|
||||||
|
}
|
||||||
case "analyze":
|
case "analyze":
|
||||||
runAnalyzer(cfg)
|
if err := app.RunAnalyzer(cfg, flag.Args()[1:], os.Stdin, os.Stdout); err != nil {
|
||||||
|
log.Fatalf("Analyzer error: %v", err)
|
||||||
|
}
|
||||||
case "version":
|
case "version":
|
||||||
fmt.Println(version)
|
fmt.Println(version)
|
||||||
default:
|
default:
|
||||||
|
|
@ -64,94 +62,11 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServer(cfg *config.Config) {
|
|
||||||
if err := cfg.Validate(); err != nil {
|
|
||||||
log.Fatalf("Invalid configuration: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize storage
|
|
||||||
store, err := storage.NewStorage(cfg.Database.Type, cfg.Database.DSN)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to initialize storage: %v", err)
|
|
||||||
}
|
|
||||||
defer store.Close()
|
|
||||||
|
|
||||||
log.Printf("Connected to %s database", cfg.Database.Type)
|
|
||||||
|
|
||||||
// Create API handler
|
|
||||||
handler := api.NewAPIHandler(store, cfg)
|
|
||||||
|
|
||||||
// Set up Gin router
|
|
||||||
if os.Getenv("GIN_MODE") == "" {
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
|
||||||
}
|
|
||||||
router := gin.Default()
|
|
||||||
|
|
||||||
// Register API routes
|
|
||||||
apiGroup := router.Group("/api")
|
|
||||||
api.RegisterHandlers(apiGroup, handler)
|
|
||||||
web.DeclareRoutes(cfg, router)
|
|
||||||
|
|
||||||
// Start server
|
|
||||||
log.Printf("Starting API server on %s", cfg.Bind)
|
|
||||||
log.Printf("Test email domain: %s", cfg.Email.Domain)
|
|
||||||
|
|
||||||
if err := router.Run(cfg.Bind); err != nil {
|
|
||||||
log.Fatalf("Failed to start server: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAnalyzer(cfg *config.Config) {
|
|
||||||
// Parse command-line flags
|
|
||||||
fs := flag.NewFlagSet("analyze", flag.ExitOnError)
|
|
||||||
recipientEmail := fs.String("recipient", "", "Recipient email address (optional, will be extracted from headers if not provided)")
|
|
||||||
fs.Parse(flag.Args()[1:])
|
|
||||||
|
|
||||||
if err := cfg.Validate(); err != nil {
|
|
||||||
log.Fatalf("Invalid configuration: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize storage
|
|
||||||
store, err := storage.NewStorage(cfg.Database.Type, cfg.Database.DSN)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to initialize storage: %v", err)
|
|
||||||
}
|
|
||||||
defer store.Close()
|
|
||||||
|
|
||||||
log.Printf("Email analyzer ready, reading from stdin...")
|
|
||||||
|
|
||||||
// Read email from stdin
|
|
||||||
emailData, err := io.ReadAll(os.Stdin)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to read email from stdin: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If recipient not provided, try to extract from headers
|
|
||||||
var recipient string
|
|
||||||
if *recipientEmail != "" {
|
|
||||||
recipient = *recipientEmail
|
|
||||||
} else {
|
|
||||||
recipient, err = receiver.ExtractRecipientFromHeaders(emailData)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to extract recipient: %v", err)
|
|
||||||
}
|
|
||||||
log.Printf("Extracted recipient: %s", recipient)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the email
|
|
||||||
recv := receiver.NewEmailReceiver(store, cfg)
|
|
||||||
if err := recv.ProcessEmailBytes(emailData, recipient); err != nil {
|
|
||||||
log.Fatalf("Failed to process email: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Email processed successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
func printUsage() {
|
func printUsage() {
|
||||||
fmt.Println("\nCommand availables:")
|
fmt.Println("\nCommand availables:")
|
||||||
fmt.Println(" happyDeliver server - Start the API server")
|
fmt.Println(" happyDeliver server - Start the API server")
|
||||||
fmt.Println(" happyDeliver analyze [-recipient EMAIL] - Analyze email from stdin (MDA mode)")
|
fmt.Println(" happyDeliver analyze [-json] - Analyze email from stdin and output results to terminal")
|
||||||
fmt.Println(" happyDeliver version - Print version information")
|
fmt.Println(" happyDeliver version - Print version information")
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
87
internal/analyzer/analyzer.go
Normal file
87
internal/analyzer/analyzer.go
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
// This file is part of the happyDeliver (R) project.
|
||||||
|
// Copyright (c) 2025 happyDomain
|
||||||
|
// Authors: Pierre-Olivier Mercier, et al.
|
||||||
|
//
|
||||||
|
// This program is offered under a commercial and under the AGPL license.
|
||||||
|
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||||
|
//
|
||||||
|
// For AGPL licensing:
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"git.happydns.org/happyDeliver/internal/api"
|
||||||
|
"git.happydns.org/happyDeliver/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EmailAnalyzer provides high-level email analysis functionality
|
||||||
|
// This is the main entry point for analyzing emails from both LMTP and CLI
|
||||||
|
type EmailAnalyzer struct {
|
||||||
|
generator *ReportGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEmailAnalyzer creates a new email analyzer with the given configuration
|
||||||
|
func NewEmailAnalyzer(cfg *config.Config) *EmailAnalyzer {
|
||||||
|
generator := NewReportGenerator(
|
||||||
|
cfg.Analysis.DNSTimeout,
|
||||||
|
cfg.Analysis.HTTPTimeout,
|
||||||
|
cfg.Analysis.RBLs,
|
||||||
|
)
|
||||||
|
|
||||||
|
return &EmailAnalyzer{
|
||||||
|
generator: generator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnalysisResult contains the complete analysis result
|
||||||
|
type AnalysisResult struct {
|
||||||
|
Email *EmailMessage
|
||||||
|
Results *AnalysisResults
|
||||||
|
Report *api.Report
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnalyzeEmailBytes performs complete email analysis from raw bytes
|
||||||
|
func (a *EmailAnalyzer) AnalyzeEmailBytes(rawEmail []byte, testID uuid.UUID) (*AnalysisResult, error) {
|
||||||
|
// Parse the email
|
||||||
|
emailMsg, err := ParseEmail(bytes.NewReader(rawEmail))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse email: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyze the email
|
||||||
|
results := a.generator.AnalyzeEmail(emailMsg)
|
||||||
|
|
||||||
|
// Generate the report
|
||||||
|
report := a.generator.GenerateReport(testID, results)
|
||||||
|
|
||||||
|
return &AnalysisResult{
|
||||||
|
Email: emailMsg,
|
||||||
|
Results: results,
|
||||||
|
Report: report,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScoreSummaryText returns a human-readable score summary
|
||||||
|
func (a *EmailAnalyzer) GetScoreSummaryText(result *AnalysisResult) string {
|
||||||
|
if result == nil || result.Results == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return a.generator.GetScoreSummaryText(result.Results)
|
||||||
|
}
|
||||||
143
internal/app/cli_analyzer.go
Normal file
143
internal/app/cli_analyzer.go
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
// This file is part of the happyDeliver (R) project.
|
||||||
|
// Copyright (c) 2025 happyDomain
|
||||||
|
// Authors: Pierre-Olivier Mercier, et al.
|
||||||
|
//
|
||||||
|
// This program is offered under a commercial and under the AGPL license.
|
||||||
|
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||||
|
//
|
||||||
|
// For AGPL licensing:
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"git.happydns.org/happyDeliver/internal/analyzer"
|
||||||
|
"git.happydns.org/happyDeliver/internal/api"
|
||||||
|
"git.happydns.org/happyDeliver/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunAnalyzer runs the standalone email analyzer (from stdin)
|
||||||
|
func RunAnalyzer(cfg *config.Config, args []string, reader io.Reader, writer io.Writer) error {
|
||||||
|
// Parse command-line flags
|
||||||
|
fs := flag.NewFlagSet("analyze", flag.ExitOnError)
|
||||||
|
jsonOutput := fs.Bool("json", false, "Output results as JSON")
|
||||||
|
if err := fs.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cfg.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Email analyzer ready, reading from stdin...")
|
||||||
|
|
||||||
|
// Read email from stdin
|
||||||
|
emailData, err := io.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read email from stdin: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create analyzer with configuration
|
||||||
|
emailAnalyzer := analyzer.NewEmailAnalyzer(cfg)
|
||||||
|
|
||||||
|
// Analyze the email (using a dummy test ID for standalone mode)
|
||||||
|
result, err := emailAnalyzer.AnalyzeEmailBytes(emailData, uuid.New())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to analyze email: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Analyzing email from: %s", result.Email.From)
|
||||||
|
|
||||||
|
// Output results
|
||||||
|
if *jsonOutput {
|
||||||
|
return outputJSON(result, writer)
|
||||||
|
}
|
||||||
|
return outputHumanReadable(result, emailAnalyzer, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// outputJSON outputs the report as JSON
|
||||||
|
func outputJSON(result *analyzer.AnalysisResult, writer io.Writer) error {
|
||||||
|
reportJSON, err := json.MarshalIndent(result.Report, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal report: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(writer, string(reportJSON))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// outputHumanReadable outputs a human-readable summary
|
||||||
|
func outputHumanReadable(result *analyzer.AnalysisResult, emailAnalyzer *analyzer.EmailAnalyzer, writer io.Writer) error {
|
||||||
|
// Header
|
||||||
|
fmt.Fprintln(writer, "\n"+strings.Repeat("=", 70))
|
||||||
|
fmt.Fprintln(writer, "EMAIL DELIVERABILITY ANALYSIS REPORT")
|
||||||
|
fmt.Fprintln(writer, strings.Repeat("=", 70))
|
||||||
|
|
||||||
|
// Score summary
|
||||||
|
summary := emailAnalyzer.GetScoreSummaryText(result)
|
||||||
|
fmt.Fprintln(writer, summary)
|
||||||
|
|
||||||
|
// Detailed checks
|
||||||
|
fmt.Fprintln(writer, "\n"+strings.Repeat("-", 70))
|
||||||
|
fmt.Fprintln(writer, "DETAILED CHECK RESULTS")
|
||||||
|
fmt.Fprintln(writer, strings.Repeat("-", 70))
|
||||||
|
|
||||||
|
// Group checks by category
|
||||||
|
categories := make(map[api.CheckCategory][]api.Check)
|
||||||
|
for _, check := range result.Report.Checks {
|
||||||
|
categories[check.Category] = append(categories[check.Category], check)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print checks by category
|
||||||
|
categoryOrder := []api.CheckCategory{
|
||||||
|
api.Authentication,
|
||||||
|
api.Dns,
|
||||||
|
api.Blacklist,
|
||||||
|
api.Content,
|
||||||
|
api.Headers,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, category := range categoryOrder {
|
||||||
|
checks, ok := categories[category]
|
||||||
|
if !ok || len(checks) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(writer, "\n%s:\n", category)
|
||||||
|
for _, check := range checks {
|
||||||
|
statusSymbol := "✓"
|
||||||
|
if check.Status == api.CheckStatusFail {
|
||||||
|
statusSymbol = "✗"
|
||||||
|
} else if check.Status == api.CheckStatusWarn {
|
||||||
|
statusSymbol = "⚠"
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(writer, " %s %s: %s\n", statusSymbol, check.Name, check.Message)
|
||||||
|
if check.Advice != nil && *check.Advice != "" {
|
||||||
|
fmt.Fprintf(writer, " → %s\n", *check.Advice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(writer, "\n"+strings.Repeat("=", 70))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
74
internal/app/server.go
Normal file
74
internal/app/server.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// This file is part of the happyDeliver (R) project.
|
||||||
|
// Copyright (c) 2025 happyDomain
|
||||||
|
// Authors: Pierre-Olivier Mercier, et al.
|
||||||
|
//
|
||||||
|
// This program is offered under a commercial and under the AGPL license.
|
||||||
|
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||||
|
//
|
||||||
|
// For AGPL licensing:
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"git.happydns.org/happyDeliver/internal/api"
|
||||||
|
"git.happydns.org/happyDeliver/internal/config"
|
||||||
|
"git.happydns.org/happyDeliver/internal/storage"
|
||||||
|
"git.happydns.org/happyDeliver/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunServer starts the API server server
|
||||||
|
func RunServer(cfg *config.Config) error {
|
||||||
|
if err := cfg.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize storage
|
||||||
|
store, err := storage.NewStorage(cfg.Database.Type, cfg.Database.DSN)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer store.Close()
|
||||||
|
|
||||||
|
log.Printf("Connected to %s database", cfg.Database.Type)
|
||||||
|
|
||||||
|
// Create API handler
|
||||||
|
handler := api.NewAPIHandler(store, cfg)
|
||||||
|
|
||||||
|
// Set up Gin router
|
||||||
|
if os.Getenv("GIN_MODE") == "" {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
}
|
||||||
|
router := gin.Default()
|
||||||
|
|
||||||
|
// Register API routes
|
||||||
|
apiGroup := router.Group("/api")
|
||||||
|
api.RegisterHandlers(apiGroup, handler)
|
||||||
|
web.DeclareRoutes(cfg, router)
|
||||||
|
|
||||||
|
// Start API server
|
||||||
|
log.Printf("Starting API server on %s", cfg.Bind)
|
||||||
|
log.Printf("Test email domain: %s", cfg.Email.Domain)
|
||||||
|
|
||||||
|
if err := router.Run(cfg.Bind); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
package receiver
|
package receiver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -39,15 +38,17 @@ import (
|
||||||
|
|
||||||
// EmailReceiver handles incoming emails from the MTA
|
// EmailReceiver handles incoming emails from the MTA
|
||||||
type EmailReceiver struct {
|
type EmailReceiver struct {
|
||||||
storage storage.Storage
|
storage storage.Storage
|
||||||
config *config.Config
|
config *config.Config
|
||||||
|
analyzer *analyzer.EmailAnalyzer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEmailReceiver creates a new email receiver
|
// NewEmailReceiver creates a new email receiver
|
||||||
func NewEmailReceiver(store storage.Storage, cfg *config.Config) *EmailReceiver {
|
func NewEmailReceiver(store storage.Storage, cfg *config.Config) *EmailReceiver {
|
||||||
return &EmailReceiver{
|
return &EmailReceiver{
|
||||||
storage: store,
|
storage: store,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
|
analyzer: analyzer.NewEmailAnalyzer(cfg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,33 +93,20 @@ func (r *EmailReceiver) ProcessEmailBytes(rawEmail []byte, recipientEmail string
|
||||||
|
|
||||||
log.Printf("Analyzing email for test %s", testID)
|
log.Printf("Analyzing email for test %s", testID)
|
||||||
|
|
||||||
// Parse the email
|
// Analyze the email using the shared analyzer
|
||||||
emailMsg, err := analyzer.ParseEmail(bytes.NewReader(rawEmail))
|
result, err := r.analyzer.AnalyzeEmailBytes(rawEmail, testID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Update test status to failed
|
// Update test status to failed
|
||||||
if updateErr := r.storage.UpdateTestStatus(testID, storage.StatusFailed); updateErr != nil {
|
if updateErr := r.storage.UpdateTestStatus(testID, storage.StatusFailed); updateErr != nil {
|
||||||
log.Printf("Failed to update test status to failed: %v", updateErr)
|
log.Printf("Failed to update test status to failed: %v", updateErr)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to parse email: %w", err)
|
return fmt.Errorf("failed to analyze email: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create report generator with configuration
|
log.Printf("Analysis complete. Score: %.2f/10", result.Report.Score)
|
||||||
generator := analyzer.NewReportGenerator(
|
|
||||||
r.config.Analysis.DNSTimeout,
|
|
||||||
r.config.Analysis.HTTPTimeout,
|
|
||||||
r.config.Analysis.RBLs,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Analyze the email
|
|
||||||
results := generator.AnalyzeEmail(emailMsg)
|
|
||||||
|
|
||||||
// Generate the report
|
|
||||||
report := generator.GenerateReport(testID, results)
|
|
||||||
|
|
||||||
log.Printf("Analysis complete. Score: %.2f/10", report.Score)
|
|
||||||
|
|
||||||
// Marshal report to JSON
|
// Marshal report to JSON
|
||||||
reportJSON, err := json.Marshal(report)
|
reportJSON, err := json.Marshal(result.Report)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Update test status to failed
|
// Update test status to failed
|
||||||
if updateErr := r.storage.UpdateTestStatus(testID, storage.StatusFailed); updateErr != nil {
|
if updateErr := r.storage.UpdateTestStatus(testID, storage.StatusFailed); updateErr != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue