Email: Review-Link auf /review/:token umgestellt; Token-Erzeugung konsolidiert. Reviews: Client-Validation hinzugefügt. Verfügbarkeiten: Auto-Update nach Regelanlage. Galerie: Cover-Foto-Flag + Setzen im Admin, sofortige Aktualisierung nach Upload/Löschen/Reihenfolge-Änderung. Startseite: Featured-Foto = Reihenfolge 0, Seitenverhältnis beibehalten, Texte aktualisiert.
This commit is contained in:
17
src/server/lib/auth.ts
Normal file
17
src/server/lib/auth.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createKV } from "./create-kv.js";
|
||||
|
||||
type Session = { id: string; userId: string; expiresAt: string; createdAt: string };
|
||||
type User = { id: string; username: string; email: string; passwordHash: string; role: "customer" | "owner"; createdAt: string };
|
||||
|
||||
export const sessionsKV = createKV<Session>("sessions");
|
||||
export const usersKV = createKV<User>("users");
|
||||
|
||||
export async function assertOwner(sessionId: string): Promise<void> {
|
||||
const session = await sessionsKV.getItem(sessionId);
|
||||
if (!session) throw new Error("Invalid session");
|
||||
if (new Date(session.expiresAt) < new Date()) throw new Error("Session expired");
|
||||
const user = await usersKV.getItem(session.userId);
|
||||
if (!user || user.role !== "owner") throw new Error("Forbidden");
|
||||
}
|
||||
|
||||
|
@@ -85,8 +85,8 @@ export async function renderBookingPendingHTML(params: { name: string; date: str
|
||||
return renderBrandedEmail("Deine Terminanfrage ist eingegangen", inner);
|
||||
}
|
||||
|
||||
export async function renderBookingConfirmedHTML(params: { name: string; date: string; time: string; cancellationUrl?: string }) {
|
||||
const { name, date, time, cancellationUrl } = params;
|
||||
export async function renderBookingConfirmedHTML(params: { name: string; date: string; time: string; cancellationUrl?: string; reviewUrl?: string }) {
|
||||
const { name, date, time, cancellationUrl, reviewUrl } = params;
|
||||
const formattedDate = formatDateGerman(date);
|
||||
const domain = process.env.DOMAIN || 'localhost:5173';
|
||||
const protocol = domain.includes('localhost') ? 'http' : 'https';
|
||||
@@ -107,6 +107,14 @@ export async function renderBookingConfirmedHTML(params: { name: string; date: s
|
||||
<a href="${cancellationUrl}" style="display: inline-block; background-color: #db2777; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 600;">Termin ansehen & verwalten</a>
|
||||
</div>
|
||||
` : ''}
|
||||
${reviewUrl ? `
|
||||
<div style="background-color: #eff6ff; border-left: 4px solid #3b82f6; padding: 16px; margin: 20px 0; border-radius: 4px;">
|
||||
<p style="margin: 0; font-weight: 600; color: #3b82f6;">⭐ Bewertung abgeben:</p>
|
||||
<p style="margin: 8px 0 12px 0; color: #475569;">Nach deinem Termin würden wir uns über deine Bewertung freuen!</p>
|
||||
<a href="${reviewUrl}" style="display: inline-block; background-color: #3b82f6; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 600;">Bewertung schreiben</a>
|
||||
<p style="margin: 12px 0 0 0; color: #64748b; font-size: 13px;">Du kannst deine Bewertung nach dem Termin über diesen Link abgeben.</p>
|
||||
</div>
|
||||
` : ''}
|
||||
<div style="background-color: #f8fafc; border-left: 4px solid #3b82f6; padding: 16px; margin: 20px 0; border-radius: 4px;">
|
||||
<p style="margin: 0; font-weight: 600; color: #3b82f6;">📋 Rechtliche Informationen:</p>
|
||||
<p style="margin: 8px 0 12px 0; color: #475569;">Weitere Informationen findest du in unserem <a href="${legalUrl}" style="color: #3b82f6; text-decoration: underline;">Impressum und Datenschutz</a>.</p>
|
||||
|
Reference in New Issue
Block a user