feat: settings and deploy docs

This commit is contained in:
opencode committed 2026-10-18 10:36:43 +02:00
1 parent 8b33242db1
commit 39f782057c
10 files changed
+194

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
import Link from "next/link";
import { getDb } from "@/lib/db";
import { DEFAULT_TAGE_VORHER } from "@/lib/settings";
import { TestButtons } from "./test-buttons";
export const dynamic = "force-dynamic";
export default async function SettingsPage() {
const db = getDb();
const last = await db.notificationLog.findFirst({ orderBy: { sentAt: "desc" } });
return (
<main className="mx-auto max-w-xl space-y-4 p-6">
<Link href="/" className="text-sm underline">← Zurück</Link>
<h1 className="text-xl font-bold">Einstellungen</h1>
<section className="rounded border p-4">
<h2 className="font-medium">Standard-Erinnerungstage</h2>
<p className="text-sm text-gray-600">Neue Gutscheine erhalten diese Erinnerungsregeln: {DEFAULT_TAGE_VORHER.join(", ")} Tage vorher.</p>
</section>
<TestButtons />
<section className="rounded border p-4 text-sm">
<h2 className="font-medium">Letzter Cron-Lauf</h2>
{last ? (
<p>
{last.sentAt.toLocaleString("de-DE")} · {last.kanal} an {last.empfaenger} · {last.status}
{last.fehler ? ` · Fehler: ${last.fehler}` : ""}
</p>
) : (
<p>Noch keine Benachrichtigung versendet.</p>
)}
</section>
<section className="rounded border p-4 text-sm text-gray-600">
<h2 className="font-medium text-black">Konfiguration (.env)</h2>
<p>SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, MAIL_FROM, TELEGRAM_BOT_TOKEN, CRON_SECRET, APP_URL.</p>
<h2 className="mt-2 font-medium text-black">Backup</h2>
<p>SQLite-Datei sichern: Volume <code>gutschein-data</code> bzw. Datei <code>/data/gutscheine.db</code> regelmäßig kopieren.</p>
</section>
</main>
);
}
+47
View File
@@ -0,0 +1,47 @@
"use client";
import { useState } from "react";
export function TestButtons() {
const [mail, setMail] = useState("test@example.de");
const [chatId, setChatId] = useState("");
const [result, setResult] = useState("");
async function testMail() {
setResult("…");
const res = await fetch("/api/settings/test-mail", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ to: mail }),
});
const data = await res.json().catch(() => ({}));
setResult(res.ok ? "Test-Mail versendet." : `Fehler: ${data.error ?? res.status}`);
}
async function testTelegram() {
setResult("…");
const res = await fetch("/api/settings/test-telegram", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ chatId }),
});
const data = await res.json().catch(() => ({}));
setResult(res.ok ? "Test-Telegram versendet." : `Fehler: ${data.error ?? res.status}`);
}
return (
<section className="space-y-3 rounded border p-4">
<h2 className="font-medium">Versand testen</h2>
<div>
<label htmlFor="mail" className="block text-sm font-medium">E-Mail-Empfänger</label>
<input id="mail" value={mail} onChange={(e) => setMail(e.target.value)} className="mt-1 w-full rounded border px-3 py-2" />
</div>
<button type="button" onClick={testMail} className="rounded bg-black px-4 py-2 text-white">Test-Mail senden</button>
<div>
<label htmlFor="chatId" className="block text-sm font-medium">Telegram-Chat-ID</label>
<input id="chatId" value={chatId} onChange={(e) => setChatId(e.target.value)} className="mt-1 w-full rounded border px-3 py-2" />
</div>
<button type="button" onClick={testTelegram} className="rounded bg-black px-4 py-2 text-white">Test-Telegram senden</button>
{result && <p role="status" className="text-sm">{result}</p>}
</section>
);
}