Extract OpenAPI schemas to separate file and move models to internal/model package
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Split api/openapi.yaml schemas into api/schemas.yaml so structs can be generated independently from the API server code. Models now generate into internal/model/ via oapi-codegen, with the server referencing them through import-mapping. Moved PtrTo helper to internal/utils and removed storage.ReportSummary in favor of model.TestSummary.
This commit is contained in:
parent
3eec5ce966
commit
396c51974a
47 changed files with 1878 additions and 1785 deletions
|
|
@ -31,7 +31,8 @@ import (
|
|||
|
||||
"golang.org/x/net/publicsuffix"
|
||||
|
||||
"git.happydns.org/happyDeliver/internal/api"
|
||||
"git.happydns.org/happyDeliver/internal/model"
|
||||
"git.happydns.org/happyDeliver/internal/utils"
|
||||
)
|
||||
|
||||
// HeaderAnalyzer analyzes email header quality and structure
|
||||
|
|
@ -43,7 +44,7 @@ func NewHeaderAnalyzer() *HeaderAnalyzer {
|
|||
}
|
||||
|
||||
// CalculateHeaderScore evaluates email structural quality from header analysis
|
||||
func (h *HeaderAnalyzer) CalculateHeaderScore(analysis *api.HeaderAnalysis) (int, rune) {
|
||||
func (h *HeaderAnalyzer) CalculateHeaderScore(analysis *model.HeaderAnalysis) (int, rune) {
|
||||
if analysis == nil || analysis.Headers == nil {
|
||||
return 0, ' '
|
||||
}
|
||||
|
|
@ -187,7 +188,7 @@ func (h *HeaderAnalyzer) parseEmailDate(dateStr string) (time.Time, error) {
|
|||
}
|
||||
|
||||
// isNoReplyAddress checks if a header check represents a no-reply email address
|
||||
func (h *HeaderAnalyzer) isNoReplyAddress(headerCheck api.HeaderCheck) bool {
|
||||
func (h *HeaderAnalyzer) isNoReplyAddress(headerCheck model.HeaderCheck) bool {
|
||||
if !headerCheck.Present || headerCheck.Value == nil {
|
||||
return false
|
||||
}
|
||||
|
|
@ -243,18 +244,18 @@ func (h *HeaderAnalyzer) formatAddress(addr *mail.Address) string {
|
|||
}
|
||||
|
||||
// GenerateHeaderAnalysis creates structured header analysis from email
|
||||
func (h *HeaderAnalyzer) GenerateHeaderAnalysis(email *EmailMessage, authResults *api.AuthenticationResults) *api.HeaderAnalysis {
|
||||
func (h *HeaderAnalyzer) GenerateHeaderAnalysis(email *EmailMessage, authResults *model.AuthenticationResults) *model.HeaderAnalysis {
|
||||
if email == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
analysis := &api.HeaderAnalysis{}
|
||||
analysis := &model.HeaderAnalysis{}
|
||||
|
||||
// Check for proper MIME structure
|
||||
analysis.HasMimeStructure = api.PtrTo(len(email.Parts) > 0)
|
||||
analysis.HasMimeStructure = utils.PtrTo(len(email.Parts) > 0)
|
||||
|
||||
// Initialize headers map
|
||||
headers := make(map[string]api.HeaderCheck)
|
||||
headers := make(map[string]model.HeaderCheck)
|
||||
|
||||
// Check required headers
|
||||
requiredHeaders := []string{"From", "To", "Date", "Message-ID", "Subject"}
|
||||
|
|
@ -308,12 +309,12 @@ func (h *HeaderAnalyzer) GenerateHeaderAnalysis(email *EmailMessage, authResults
|
|||
}
|
||||
|
||||
// checkHeader checks if a header is present and valid
|
||||
func (h *HeaderAnalyzer) checkHeader(email *EmailMessage, headerName string, importance string) *api.HeaderCheck {
|
||||
func (h *HeaderAnalyzer) checkHeader(email *EmailMessage, headerName string, importance string) *model.HeaderCheck {
|
||||
value := email.GetHeaderValue(headerName)
|
||||
present := email.HasHeader(headerName) && value != ""
|
||||
|
||||
importanceEnum := api.HeaderCheckImportance(importance)
|
||||
check := &api.HeaderCheck{
|
||||
importanceEnum := model.HeaderCheckImportance(importance)
|
||||
check := &model.HeaderCheck{
|
||||
Present: present,
|
||||
Importance: &importanceEnum,
|
||||
}
|
||||
|
|
@ -374,10 +375,10 @@ func (h *HeaderAnalyzer) checkHeader(email *EmailMessage, headerName string, imp
|
|||
}
|
||||
|
||||
// analyzeDomainAlignment checks domain alignment between headers and DKIM signatures
|
||||
func (h *HeaderAnalyzer) analyzeDomainAlignment(email *EmailMessage, authResults *api.AuthenticationResults) *api.DomainAlignment {
|
||||
alignment := &api.DomainAlignment{
|
||||
Aligned: api.PtrTo(true),
|
||||
RelaxedAligned: api.PtrTo(true),
|
||||
func (h *HeaderAnalyzer) analyzeDomainAlignment(email *EmailMessage, authResults *model.AuthenticationResults) *model.DomainAlignment {
|
||||
alignment := &model.DomainAlignment{
|
||||
Aligned: utils.PtrTo(true),
|
||||
RelaxedAligned: utils.PtrTo(true),
|
||||
}
|
||||
|
||||
// Extract From domain
|
||||
|
|
@ -405,13 +406,13 @@ func (h *HeaderAnalyzer) analyzeDomainAlignment(email *EmailMessage, authResults
|
|||
}
|
||||
|
||||
// Extract DKIM domains from authentication results
|
||||
var dkimDomains []api.DKIMDomainInfo
|
||||
var dkimDomains []model.DKIMDomainInfo
|
||||
if authResults != nil && authResults.Dkim != nil {
|
||||
for _, dkim := range *authResults.Dkim {
|
||||
if dkim.Domain != nil && *dkim.Domain != "" {
|
||||
domain := *dkim.Domain
|
||||
orgDomain := h.getOrganizationalDomain(domain)
|
||||
dkimDomains = append(dkimDomains, api.DKIMDomainInfo{
|
||||
dkimDomains = append(dkimDomains, model.DKIMDomainInfo{
|
||||
Domain: domain,
|
||||
OrgDomain: orgDomain,
|
||||
})
|
||||
|
|
@ -560,18 +561,18 @@ func (h *HeaderAnalyzer) getOrganizationalDomain(domain string) string {
|
|||
}
|
||||
|
||||
// findHeaderIssues identifies issues with headers
|
||||
func (h *HeaderAnalyzer) findHeaderIssues(email *EmailMessage) []api.HeaderIssue {
|
||||
var issues []api.HeaderIssue
|
||||
func (h *HeaderAnalyzer) findHeaderIssues(email *EmailMessage) []model.HeaderIssue {
|
||||
var issues []model.HeaderIssue
|
||||
|
||||
// Check for missing required headers
|
||||
requiredHeaders := []string{"From", "Date", "Message-ID"}
|
||||
for _, header := range requiredHeaders {
|
||||
if !email.HasHeader(header) || email.GetHeaderValue(header) == "" {
|
||||
issues = append(issues, api.HeaderIssue{
|
||||
issues = append(issues, model.HeaderIssue{
|
||||
Header: header,
|
||||
Severity: api.HeaderIssueSeverityCritical,
|
||||
Severity: model.HeaderIssueSeverityCritical,
|
||||
Message: fmt.Sprintf("Required header '%s' is missing", header),
|
||||
Advice: api.PtrTo(fmt.Sprintf("Add the %s header to ensure RFC 5322 compliance", header)),
|
||||
Advice: utils.PtrTo(fmt.Sprintf("Add the %s header to ensure RFC 5322 compliance", header)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -579,11 +580,11 @@ func (h *HeaderAnalyzer) findHeaderIssues(email *EmailMessage) []api.HeaderIssue
|
|||
// Check Message-ID format
|
||||
messageID := email.GetHeaderValue("Message-ID")
|
||||
if messageID != "" && !h.isValidMessageID(messageID) {
|
||||
issues = append(issues, api.HeaderIssue{
|
||||
issues = append(issues, model.HeaderIssue{
|
||||
Header: "Message-ID",
|
||||
Severity: api.HeaderIssueSeverityMedium,
|
||||
Severity: model.HeaderIssueSeverityMedium,
|
||||
Message: "Message-ID format is invalid",
|
||||
Advice: api.PtrTo("Use proper Message-ID format: <unique-id@domain.com>"),
|
||||
Advice: utils.PtrTo("Use proper Message-ID format: <unique-id@domain.com>"),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -591,7 +592,7 @@ func (h *HeaderAnalyzer) findHeaderIssues(email *EmailMessage) []api.HeaderIssue
|
|||
}
|
||||
|
||||
// parseReceivedChain extracts the chain of Received headers from an email
|
||||
func (h *HeaderAnalyzer) parseReceivedChain(email *EmailMessage) []api.ReceivedHop {
|
||||
func (h *HeaderAnalyzer) parseReceivedChain(email *EmailMessage) []model.ReceivedHop {
|
||||
if email == nil || email.Header == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -601,7 +602,7 @@ func (h *HeaderAnalyzer) parseReceivedChain(email *EmailMessage) []api.ReceivedH
|
|||
return nil
|
||||
}
|
||||
|
||||
var chain []api.ReceivedHop
|
||||
var chain []model.ReceivedHop
|
||||
|
||||
for _, receivedValue := range receivedHeaders {
|
||||
hop := h.parseReceivedHeader(receivedValue)
|
||||
|
|
@ -614,8 +615,8 @@ func (h *HeaderAnalyzer) parseReceivedChain(email *EmailMessage) []api.ReceivedH
|
|||
}
|
||||
|
||||
// parseReceivedHeader parses a single Received header value
|
||||
func (h *HeaderAnalyzer) parseReceivedHeader(receivedValue string) *api.ReceivedHop {
|
||||
hop := &api.ReceivedHop{}
|
||||
func (h *HeaderAnalyzer) parseReceivedHeader(receivedValue string) *model.ReceivedHop {
|
||||
hop := &model.ReceivedHop{}
|
||||
|
||||
// Normalize whitespace - Received headers can span multiple lines
|
||||
normalized := strings.Join(strings.Fields(receivedValue), " ")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue