diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5451490..07656fa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,9 +31,17 @@ jobs: tar -czf "nexredirect-next-${TAG}.tar.gz" .next ls -lh "nexredirect-next-${TAG}.tar.gz" + - name: Compute SHA256 + run: | + TAG="${GITHUB_REF_NAME}" + sha256sum "nexredirect-next-${TAG}.tar.gz" > "nexredirect-checksums-${TAG}.txt" + cat "nexredirect-checksums-${TAG}.txt" + - name: Attach to release uses: softprops/action-gh-release@v2 with: - files: nexredirect-next-*.tar.gz + files: | + nexredirect-next-*.tar.gz + nexredirect-checksums-*.txt tag_name: ${{ github.ref_name }} fail_on_unmatched_files: true diff --git a/app/(app)/settings/page.tsx b/app/(app)/settings/page.tsx index 93fe47f..6cec283 100644 --- a/app/(app)/settings/page.tsx +++ b/app/(app)/settings/page.tsx @@ -1,6 +1,6 @@ "use client"; import { useEffect, useState } from "react"; -import { Loader2, RefreshCcw, ArrowUpCircle, Globe2, CheckCircle2, Trash2 } from "lucide-react"; +import { Loader2, RefreshCcw, ArrowUpCircle, Globe2, CheckCircle2, Trash2, Mail, Send } from "lucide-react"; import { PageHeader } from "@/components/PageHeader"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; @@ -34,20 +34,51 @@ export default function SettingsPage() { const [accountId, setAccountId] = useState(""); const [installingGeo, setInstallingGeo] = useState(false); const [geoMsg, setGeoMsg] = useState(""); + const [smtp, setSmtp] = useState<{ smtp_host: string; smtp_port: string; smtp_user: string; smtp_password: string; smtp_from: string; smtp_secure: string }>({ smtp_host: "", smtp_port: "587", smtp_user: "", smtp_password: "", smtp_from: "", smtp_secure: "false" }); + const [smtpSaving, setSmtpSaving] = useState(false); + const [smtpTestTo, setSmtpTestTo] = useState(""); + const [smtpTestBusy, setSmtpTestBusy] = useState(false); + const [smtpTestMsg, setSmtpTestMsg] = useState(""); const [saving, setSaving] = useState(false); const [checking, setChecking] = useState(false); const [applying, setApplying] = useState(false); const [msg, setMsg] = useState(""); async function load() { - const [s, u, g] = await Promise.all([ + const [s, u, g, m] = await Promise.all([ fetch("/api/settings").then((r) => r.json()), fetch("/api/update/check").then((r) => r.json()), fetch("/api/settings/geo").then((r) => r.json()), + fetch("/api/settings/smtp").then((r) => r.json()), ]); setSettings(s); setStatus(u); setGeo(g); + setSmtp({ + smtp_host: m.smtp_host || "", + smtp_port: m.smtp_port || "587", + smtp_user: m.smtp_user || "", + smtp_password: m.smtp_password || "", + smtp_from: m.smtp_from || "", + smtp_secure: m.smtp_secure || "false", + }); + } + + async function saveSmtp() { + setSmtpSaving(true); + try { + await fetch("/api/settings/smtp", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(smtp) }); + } finally { setSmtpSaving(false); } + } + + async function sendTestMail() { + setSmtpTestBusy(true); + setSmtpTestMsg(""); + try { + const r = await fetch("/api/settings/smtp", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ to: smtpTestTo || undefined }) }); + const d = await r.json(); + setSmtpTestMsg(d.ok ? "Test-Mail verschickt." : `Fehler: ${d.error}`); + } finally { setSmtpTestBusy(false); } } useEffect(() => { load(); }, []); @@ -332,6 +363,62 @@ export default function SettingsPage() { {geoMsg &&
{geoMsg}
} + +{smtpTestMsg}
} +