Update features on home page

This commit is contained in:
nemunaire 2025-10-24 17:35:58 +07:00
commit edb172c4bc
3 changed files with 101 additions and 13 deletions

View file

@ -54,6 +54,10 @@ func init() {
func DeclareRoutes(cfg *config.Config, router *gin.Engine) {
appConfig := map[string]interface{}{}
if cfg.ReportRetention > 0 {
appConfig["report_retention"] = cfg.ReportRetention
}
if appcfg, err := json.MarshalIndent(appConfig, "", " "); err != nil {
log.Println("Unable to generate JSON config to inject in web application")
} else {

48
web/src/lib/config.ts Normal file
View file

@ -0,0 +1,48 @@
// 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/>.
import { writable } from "svelte/store";
interface AppConfig {
report_retention?: number;
}
const defaultConfig: AppConfig = {
report_retention: 0,
};
function getConfigFromScriptTag(): AppConfig | null {
if (typeof document !== "undefined") {
const configScript = document.getElementById("app-config");
if (configScript) {
try {
return JSON.parse(configScript.textContent || "");
} catch (e) {
console.error("Failed to parse app config:", e);
}
}
}
return null;
}
const initialConfig = getConfigFromScriptTag() || defaultConfig;
export const appConfig = writable<AppConfig>(initialConfig);

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { goto } from "$app/navigation";
import { createTest as apiCreateTest } from "$lib/api";
import { appConfig } from "$lib/config";
import { FeatureCard, HowItWorksStep } from "$lib/components";
let loading = $state(false);
@ -21,7 +22,27 @@
}
}
const features = [
function getRetentionTimeText(): string {
if (!$appConfig.report_retention) return "ever";
const seconds = $appConfig.report_retention / 1000000000;
const days = Math.floor(seconds / 86400);
const weeks = Math.floor(days / 7);
const months = Math.floor(days / 30);
if (months >= 1) {
return months === 1 ? "1 month" : `${months} months`;
} else if (weeks >= 1) {
return weeks === 1 ? "1 week" : `${weeks} weeks`;
} else if (days >= 1) {
return days === 1 ? "1 day" : `${days} days`;
} else {
const hours = Math.floor(seconds / 3600);
return hours === 1 ? "1 hour" : `${hours} hours`;
}
}
const features = $derived([
{
icon: "bi-shield-check",
title: "Authentication",
@ -30,16 +51,31 @@
variant: "primary" as const,
},
{
icon: "bi-patch-check",
icon: "bi-building-check",
title: "BIMI Support",
description:
"Brand Indicators for Message Identification - verify your brand logo configuration.",
variant: "info" as const,
},
{
icon: "bi-link-45deg",
title: "ARC Verification",
description:
"Authenticated Received Chain validation for forwarded emails and mailing lists.",
variant: "primary" as const,
},
{
icon: "bi-check2-circle",
title: "Domain Alignment",
description:
"Verify alignment between From, Return-Path, and DKIM domains for DMARC compliance.",
variant: "success" as const,
},
{
icon: "bi-globe",
title: "DNS Records",
description: "Verify MX, SPF, DKIM, DMARC, and BIMI records are properly configured.",
description:
"Verify PTR, MX, SPF, DKIM, DMARC, and BIMI records are properly configured.",
variant: "success" as const,
},
{
@ -54,32 +90,32 @@
description: "Check if your IP is listed in major DNS-based blacklists (RBLs).",
variant: "danger" as const,
},
{
icon: "bi-file-text",
title: "Content Analysis",
description: "HTML structure, link validation, image analysis, and more.",
variant: "info" as const,
},
{
icon: "bi-card-heading",
title: "Header Quality",
description: "Validate required headers, check for missing fields and alignment.",
variant: "secondary" as const,
},
{
icon: "bi-file-text",
title: "Content Analysis",
description: "HTML structure, link validation, image analysis, and more.",
variant: "info" as const,
},
{
icon: "bi-bar-chart",
title: "Detailed Scoring",
description:
"0-10 deliverability score with breakdown by category and recommendations.",
"A to F deliverability grade with breakdown by category and recommendations.",
variant: "primary" as const,
},
{
icon: "bi-lock",
title: "Privacy First",
description: "Self-hosted solution, your data never leaves your infrastructure.",
description: `Self-hosted solution, your data never leaves your infrastructure. Reports retained for ${getRetentionTimeText()}.`,
variant: "success" as const,
},
];
]);
const steps = [
{
@ -152,7 +188,7 @@
</div>
</div>
<div class="row g-4">
<div class="row g-4 justify-content-center">
{#each features as feature}
<div class="col-md-6 col-lg-3">
<FeatureCard {...feature} />