diff --git a/app/auth/sign-in/page.tsx b/app/auth/sign-in/page.tsx index af732f7..b7c0682 100644 --- a/app/auth/sign-in/page.tsx +++ b/app/auth/sign-in/page.tsx @@ -10,7 +10,9 @@ export default function SignInPage() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); + const [magicLinkMessage, setMagicLinkMessage] = useState(""); const [loading, setLoading] = useState(false); + const [magicLinkLoading, setMagicLinkLoading] = useState(false); const handleSignIn = async (e: React.FormEvent) => { e.preventDefault(); @@ -35,6 +37,35 @@ export default function SignInPage() { } }; + const handleMagicLink = async () => { + setError(""); + setMagicLinkMessage(""); + + if (!email) { + setError("Bitte gib deine E-Mail-Adresse ein"); + return; + } + + setMagicLinkLoading(true); + try { + const result = await authClient.signIn.magicLink({ + email, + callbackURL: "/dashboard", + errorCallbackURL: "/auth/sign-in", + }); + + if (result.error) { + setError(result.error.message ?? "Magic Link konnte nicht gesendet werden"); + } else { + setMagicLinkMessage("Magic Link gesendet. Prüfe dein Postfach."); + } + } catch { + setError("Ein unerwarteter Fehler ist aufgetreten"); + } finally { + setMagicLinkLoading(false); + } + }; + return (
{magicLinkMessage}
+ )}
diff --git a/convex/CLAUDE.md b/convex/CLAUDE.md
index f83b394..585bfcc 100644
--- a/convex/CLAUDE.md
+++ b/convex/CLAUDE.md
@@ -119,7 +119,11 @@ Wirft bei unauthentifiziertem Zugriff. Wird von allen Queries und Mutations genu
### Auth-Race-Härtung
- `canvases.get` nutzt optionalen Auth-Check und gibt bei fehlender Session `null` zurück (statt Throw), damit SSR/Client-Hydration bei kurzem Token-Race nicht in `404` kippt.
+- `canvases.list` gibt bei fehlender Session eine leere Liste zurück (statt Throw), damit Dashboard-Subscriptions beim Logout keinen Error-Spam erzeugen.
- `credits.getBalance` gibt bei fehlender Session einen Default-Stand (`0`-Werte) zurück (statt Throw), damit UI-Widgets nicht mit `Unauthenticated` fehlschlagen.
+- `credits.getSubscription` fällt bei fehlender Session auf Free/Active zurück (statt Throw), damit Tier-UI stabil bleibt.
+- `credits.getRecentTransactions` gibt bei fehlender Session `[]` zurück (statt Throw), damit Aktivitätslisten beim Logout sauber leeren.
+- `credits.getUsageStats` gibt bei fehlender Session `0`-Statistiken zurück (statt Throw), damit Verbrauchsanzeigen ohne Fehler ausrendern.
### Idempotente Canvas-Mutations
diff --git a/convex/auth.ts b/convex/auth.ts
index 97f9365..42bc6d2 100644
--- a/convex/auth.ts
+++ b/convex/auth.ts
@@ -8,6 +8,7 @@ import { internal } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
import { query } from "./_generated/server";
import { betterAuth } from "better-auth/minimal";
+import { magicLink } from "better-auth/plugins";
import { Resend } from "resend";
import authConfig from "./auth.config";
@@ -26,6 +27,9 @@ export const authComponent = createClient Klicke auf den Button, um dich anzumelden:
+ Der Link ist 10 Minuten gültig. Falls der Button nicht funktioniert, kopiere diesen Link:Dein Login-Link für LemonSpace 🍋
+
+ ${magicLinkUrl.toString()}
+