diff --git a/api/openapi.yaml b/api/openapi.yaml index ee56cff..225e26c 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -76,49 +76,6 @@ paths: schema: $ref: '#/components/schemas/Error' - /tests: - get: - tags: - - tests - summary: List all tests - description: Returns a paginated list of test summaries with scores and grades. Can be disabled via server configuration. - operationId: listTests - parameters: - - name: offset - in: query - schema: - type: integer - minimum: 0 - default: 0 - description: Number of items to skip - - name: limit - in: query - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - description: Maximum number of items to return - responses: - '200': - description: List of test summaries - content: - application/json: - schema: - $ref: '#/components/schemas/TestListResponse' - '403': - description: Test listing is disabled - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /report/{id}: get: tags: @@ -1408,53 +1365,3 @@ components: items: $ref: '#/components/schemas/BlacklistCheck' description: List of DNS whitelist check results (informational only) - - TestSummary: - type: object - required: - - test_id - - score - - grade - - created_at - properties: - test_id: - type: string - pattern: '^[a-z0-9-]+$' - description: Test identifier (base32-encoded with hyphens) - score: - type: integer - minimum: 0 - maximum: 100 - description: Overall deliverability score (0-100) - grade: - type: string - enum: [A+, A, B, C, D, E, F] - description: Letter grade - from_domain: - type: string - description: Sender domain extracted from the report - created_at: - type: string - format: date-time - - TestListResponse: - type: object - required: - - tests - - total - - offset - - limit - properties: - tests: - type: array - items: - $ref: '#/components/schemas/TestSummary' - total: - type: integer - description: Total number of tests - offset: - type: integer - description: Current offset - limit: - type: integer - description: Current limit diff --git a/go.mod b/go.mod index 2cb24a7..81bcc05 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/emersion/go-smtp v0.24.0 github.com/gin-gonic/gin v1.12.0 github.com/google/uuid v1.6.0 - github.com/oapi-codegen/runtime v1.4.0 + github.com/oapi-codegen/runtime v1.3.1 golang.org/x/net v0.52.0 gorm.io/driver/postgres v1.6.0 gorm.io/driver/sqlite v1.6.0 diff --git a/go.sum b/go.sum index a79c0ba..073f0cf 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oapi-codegen/oapi-codegen/v2 v2.5.1 h1:5vHNY1uuPBRBWqB2Dp0G7YB03phxLQZupZTIZaeorjc= github.com/oapi-codegen/oapi-codegen/v2 v2.5.1/go.mod h1:ro0npU1BWkcGpCgGD9QwPp44l5OIZ94tB3eabnT7DjQ= -github.com/oapi-codegen/runtime v1.4.0 h1:KLOSFOp7UzkbS7Cs1ms6NBEKYr0WmH2wZG0KKbd2er4= -github.com/oapi-codegen/runtime v1.4.0/go.mod h1:5sw5fxCDmnOzKNYmkVNF8d34kyUeejJEY8HNT2WaPec= +github.com/oapi-codegen/runtime v1.3.1 h1:RgDY6J4OGQLbRXhG/Xpt3vSVqYpHQS7hN4m85+5xB9g= +github.com/oapi-codegen/runtime v1.3.1/go.mod h1:kOdeacKy7t40Rclb1je37ZLFboFxh+YLy0zaPCMibPY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= diff --git a/internal/api/handlers.go b/internal/api/handlers.go index e524b40..470136e 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -381,78 +381,3 @@ func (h *APIHandler) CheckBlacklist(c *gin.Context) { c.JSON(http.StatusOK, response) } - -// ListTests returns a paginated list of test summaries -// (GET /tests) -func (h *APIHandler) ListTests(c *gin.Context, params ListTestsParams) { - if h.config.DisableTestList { - c.JSON(http.StatusForbidden, Error{ - Error: "feature_disabled", - Message: "Test listing is disabled on this instance", - }) - return - } - - offset := 0 - limit := 20 - if params.Offset != nil { - offset = *params.Offset - } - if params.Limit != nil { - limit = *params.Limit - if limit > 100 { - limit = 100 - } - } - - summaries, total, err := h.storage.ListReportSummaries(offset, limit) - if err != nil { - c.JSON(http.StatusInternalServerError, Error{ - Error: "internal_error", - Message: "Failed to list tests", - Details: stringPtr(err.Error()), - }) - return - } - - tests := make([]TestSummary, 0, len(summaries)) - for _, s := range summaries { - base32ID := utils.UUIDToBase32(s.TestID) - - var grade TestSummaryGrade - switch s.Grade { - case "A+": - grade = TestSummaryGradeA - case "A": - grade = TestSummaryGradeA1 - case "B": - grade = TestSummaryGradeB - case "C": - grade = TestSummaryGradeC - case "D": - grade = TestSummaryGradeD - case "E": - grade = TestSummaryGradeE - default: - grade = TestSummaryGradeF - } - - summary := TestSummary{ - TestId: base32ID, - Score: s.Score, - Grade: grade, - CreatedAt: s.CreatedAt, - } - if s.FromDomain != "" { - summary.FromDomain = stringPtr(s.FromDomain) - } - tests = append(tests, summary) - } - - c.JSON(http.StatusOK, TestListResponse{ - Tests: tests, - Total: int(total), - Offset: offset, - Limit: limit, - }) -} diff --git a/internal/config/cli.go b/internal/config/cli.go index fcc914f..77108ca 100644 --- a/internal/config/cli.go +++ b/internal/config/cli.go @@ -44,7 +44,6 @@ func declareFlags(o *Config) { flag.UintVar(&o.RateLimit, "rate-limit", o.RateLimit, "API rate limit (requests per second per IP)") flag.Var(&URL{&o.SurveyURL}, "survey-url", "URL for user feedback survey") flag.StringVar(&o.CustomLogoURL, "custom-logo-url", o.CustomLogoURL, "URL for custom logo image in the web UI") - flag.BoolVar(&o.DisableTestList, "disable-test-list", o.DisableTestList, "Disable the public test listing endpoint") // Others flags are declared in some other files likes sources, storages, ... when they need specials configurations } diff --git a/internal/config/config.go b/internal/config/config.go index b264994..9d803d0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -50,7 +50,6 @@ type Config struct { RateLimit uint // API rate limit (requests per second per IP) SurveyURL url.URL // URL for user feedback survey CustomLogoURL string // URL for custom logo image in the web UI - DisableTestList bool // Disable the public test listing endpoint } // DatabaseConfig contains database connection settings diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 1077e74..39b2eb6 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -45,21 +45,11 @@ type Storage interface { ReportExists(testID uuid.UUID) (bool, error) UpdateReport(testID uuid.UUID, reportJSON []byte) error DeleteOldReports(olderThan time.Time) (int64, error) - ListReportSummaries(offset, limit int) ([]ReportSummary, int64, error) // Close closes the database connection Close() error } -// ReportSummary is a lightweight projection of Report for listing -type ReportSummary struct { - TestID uuid.UUID - Score int - Grade string - FromDomain string - CreatedAt time.Time -} - // DBStorage implements Storage using GORM type DBStorage struct { db *gorm.DB @@ -149,47 +139,6 @@ func (s *DBStorage) DeleteOldReports(olderThan time.Time) (int64, error) { return result.RowsAffected, nil } -// ListReportSummaries returns a paginated list of lightweight report summaries -func (s *DBStorage) ListReportSummaries(offset, limit int) ([]ReportSummary, int64, error) { - var total int64 - if err := s.db.Model(&Report{}).Count(&total).Error; err != nil { - return nil, 0, fmt.Errorf("failed to count reports: %w", err) - } - - if total == 0 { - return []ReportSummary{}, 0, nil - } - - var selectExpr string - switch s.db.Dialector.Name() { - case "postgres": - selectExpr = `test_id, ` + - `(convert_from(report_json, 'UTF8')::jsonb->>'score')::int as score, ` + - `convert_from(report_json, 'UTF8')::jsonb->>'grade' as grade, ` + - `convert_from(report_json, 'UTF8')::jsonb->'dns_results'->>'from_domain' as from_domain, ` + - `created_at` - default: // sqlite - selectExpr = `test_id, ` + - `json_extract(report_json, '$.score') as score, ` + - `json_extract(report_json, '$.grade') as grade, ` + - `json_extract(report_json, '$.dns_results.from_domain') as from_domain, ` + - `created_at` - } - - var summaries []ReportSummary - err := s.db.Model(&Report{}). - Select(selectExpr). - Order("created_at DESC"). - Offset(offset). - Limit(limit). - Scan(&summaries).Error - if err != nil { - return nil, 0, fmt.Errorf("failed to list report summaries: %w", err) - } - - return summaries, total, nil -} - // Close closes the database connection func (s *DBStorage) Close() error { sqlDB, err := s.db.DB() diff --git a/web/routes.go b/web/routes.go index 056115d..876954c 100644 --- a/web/routes.go +++ b/web/routes.go @@ -70,10 +70,6 @@ func DeclareRoutes(cfg *config.Config, router *gin.Engine) { appConfig["custom_logo_url"] = cfg.CustomLogoURL } - if !cfg.DisableTestList { - appConfig["test_list_enabled"] = true - } - if appcfg, err := json.MarshalIndent(appConfig, "", " "); err != nil { log.Println("Unable to generate JSON config to inject in web application") } else { @@ -99,7 +95,6 @@ func DeclareRoutes(cfg *config.Config, router *gin.Engine) { router.GET("/domain/:domain", serveOrReverse("/", cfg)) router.GET("/test/", serveOrReverse("/", cfg)) router.GET("/test/:testid", serveOrReverse("/", cfg)) - router.GET("/history/", serveOrReverse("/", cfg)) router.GET("/favicon.png", func(c *gin.Context) { c.Writer.Header().Set("Cache-Control", "public, max-age=604800, immutable") }, serveOrReverse("", cfg)) router.GET("/img/*path", serveOrReverse("", cfg)) diff --git a/web/src/lib/components/HeaderAnalysisCard.svelte b/web/src/lib/components/HeaderAnalysisCard.svelte index 73c39e8..b26b492 100644 --- a/web/src/lib/components/HeaderAnalysisCard.svelte +++ b/web/src/lib/components/HeaderAnalysisCard.svelte @@ -11,7 +11,7 @@ headerScore?: number; } - let { dmarcRecord, headerAnalysis, headerGrade, headerScore }: Props = $props(); + let { dmarcRecord, headerAnalysis, headerGrade, headerScore, xAlignedFrom }: Props = $props();
| Grade | -Score | -Domain | -Date | -- |
|---|---|---|---|---|
|
- |
- - {test.score}% - | -
- {#if test.from_domain}
- {test.from_domain}
- {:else}
- -
- {/if}
- |
- - {formatDate(test.created_at)} - | -- - | -
Loading tests...
-- Send a test email to get your first deliverability - report. -
- -