This commit is contained in:
commit
59bb5a38a3
132 files changed
+19435
No files matched your search
@@ -0,0 +1,152 @@
|
||||
"use server";
|
||||
|
||||
/**
|
||||
* Server-Actions des Adminbereichs. JEDE Action prueft zuerst selbst die
|
||||
* Anmeldung inkl. 2FA (requireAdminAction) — unabhaengig davon, ob die
|
||||
* Seite geschuetzt ist, denn Actions sind eigenstaendig aufrufbar.
|
||||
*/
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { adminActor, requireAdminAction } from "@/lib/admin-auth";
|
||||
import {
|
||||
campInputSchema,
|
||||
cancelBooking,
|
||||
confirmReviewedBooking,
|
||||
refundInstallment,
|
||||
resendConfirmation,
|
||||
saveVoucher,
|
||||
updateCamp,
|
||||
voucherInputSchema,
|
||||
type ActionResult,
|
||||
} from "@/lib/admin-service";
|
||||
import { getDb } from "@/lib/db";
|
||||
import { requireAppSecret } from "@/lib/env";
|
||||
import { getGateway } from "@/lib/payment-gateway";
|
||||
|
||||
function back(path: string, result: ActionResult): never {
|
||||
const param = result.ok ? "ok" : "fehler";
|
||||
redirect(`${path}?${param}=${encodeURIComponent(result.message)}`);
|
||||
}
|
||||
|
||||
function str(form: FormData, key: string): string {
|
||||
const value = form.get(key);
|
||||
return typeof value === "string" ? value : "";
|
||||
}
|
||||
|
||||
function bool(form: FormData, key: string): boolean {
|
||||
return form.get(key) === "on";
|
||||
}
|
||||
|
||||
function firstIssue(error: { issues: { message: string }[] }): string {
|
||||
return error.issues[0]?.message ?? "Ungültige Eingabe.";
|
||||
}
|
||||
|
||||
const ID = /^[A-Za-z0-9_-]{8,64}$/;
|
||||
|
||||
export async function cancelBookingAction(form: FormData) {
|
||||
const admin = await requireAdminAction();
|
||||
const id = str(form, "bookingId");
|
||||
if (!ID.test(id)) back("/admin/buchungen", { ok: false, message: "Ungültige Buchung." });
|
||||
const result = await cancelBooking(getDb(), getGateway(), { bookingId: id, reason: str(form, "reason") }, adminActor(admin));
|
||||
revalidatePath(`/admin/buchungen/${id}`);
|
||||
back(`/admin/buchungen/${id}`, result);
|
||||
}
|
||||
|
||||
export async function refundAction(form: FormData) {
|
||||
const admin = await requireAdminAction();
|
||||
const bookingId = str(form, "bookingId");
|
||||
const installmentId = str(form, "installmentId");
|
||||
const requestKey = str(form, "requestKey");
|
||||
if (!ID.test(bookingId) || !ID.test(installmentId) || !/^[0-9a-f-]{36}$/.test(requestKey)) {
|
||||
back("/admin/buchungen", { ok: false, message: "Ungültige Anfrage." });
|
||||
}
|
||||
const euros = Number(str(form, "amount").replace(",", "."));
|
||||
const amountCents = Math.round(euros * 100);
|
||||
const result = await refundInstallment(
|
||||
getDb(),
|
||||
getGateway(),
|
||||
{ installmentId, amountCents, requestKey, reason: str(form, "reason") },
|
||||
adminActor(admin),
|
||||
);
|
||||
revalidatePath(`/admin/buchungen/${bookingId}`);
|
||||
back(`/admin/buchungen/${bookingId}`, result);
|
||||
}
|
||||
|
||||
export async function confirmReviewAction(form: FormData) {
|
||||
const admin = await requireAdminAction();
|
||||
const id = str(form, "bookingId");
|
||||
if (!ID.test(id)) back("/admin/buchungen", { ok: false, message: "Ungültige Buchung." });
|
||||
const result = await confirmReviewedBooking(getDb(), id, adminActor(admin));
|
||||
revalidatePath(`/admin/buchungen/${id}`);
|
||||
back(`/admin/buchungen/${id}`, result);
|
||||
}
|
||||
|
||||
export async function resendConfirmationAction(form: FormData) {
|
||||
const admin = await requireAdminAction();
|
||||
const id = str(form, "bookingId");
|
||||
if (!ID.test(id)) back("/admin/buchungen", { ok: false, message: "Ungültige Buchung." });
|
||||
const result = await resendConfirmation(getDb(), id, requireAppSecret(), adminActor(admin));
|
||||
back(`/admin/buchungen/${id}`, result);
|
||||
}
|
||||
|
||||
export async function saveVoucherAction(form: FormData) {
|
||||
const admin = await requireAdminAction();
|
||||
const id = str(form, "voucherId") || null;
|
||||
if (id && !ID.test(id)) back("/admin/gutscheine", { ok: false, message: "Ungültiger Gutschein." });
|
||||
const parsed = voucherInputSchema.safeParse({
|
||||
code: str(form, "code"),
|
||||
active: bool(form, "active"),
|
||||
validFrom: str(form, "validFrom"),
|
||||
validUntil: str(form, "validUntil"),
|
||||
maxRedemptions: str(form, "maxRedemptions"),
|
||||
boundEmail: str(form, "boundEmail"),
|
||||
internalNote: str(form, "internalNote"),
|
||||
});
|
||||
const target = id ? `/admin/gutscheine/${id}` : "/admin/gutscheine";
|
||||
if (!parsed.success) back(target, { ok: false, message: firstIssue(parsed.error) });
|
||||
const result = await saveVoucher(getDb(), id, parsed.data, adminActor(admin));
|
||||
revalidatePath("/admin/gutscheine");
|
||||
back(result.ok && result.id ? `/admin/gutscheine/${result.id}` : target, result);
|
||||
}
|
||||
|
||||
export async function updateCampAction(form: FormData) {
|
||||
const admin = await requireAdminAction();
|
||||
const parsed = campInputSchema.safeParse({
|
||||
salesEnabled: bool(form, "salesEnabled"),
|
||||
capacity: str(form, "capacity"),
|
||||
capacityConfirmed: bool(form, "capacityConfirmed"),
|
||||
stammmodelQuota: str(form, "stammmodelQuota"),
|
||||
stammmodelQuotaConfirmed: bool(form, "stammmodelQuotaConfirmed"),
|
||||
campStartDate: str(form, "campStartDate"),
|
||||
campEndDate: str(form, "campEndDate"),
|
||||
dateConfirmed: bool(form, "dateConfirmed"),
|
||||
locationName: str(form, "locationName"),
|
||||
locationRegion: str(form, "locationRegion"),
|
||||
locationConfirmed: bool(form, "locationConfirmed"),
|
||||
accommodationText: str(form, "accommodationText"),
|
||||
accommodationConfirmed: bool(form, "accommodationConfirmed"),
|
||||
servicesConfirmed: bool(form, "servicesConfirmed"),
|
||||
organizerName: str(form, "organizerName"),
|
||||
organizerAddress: str(form, "organizerAddress"),
|
||||
organizerEmail: str(form, "organizerEmail"),
|
||||
organizerPhone: str(form, "organizerPhone"),
|
||||
organizerConfirmed: bool(form, "organizerConfirmed"),
|
||||
taxNote: str(form, "taxNote"),
|
||||
taxConfirmed: bool(form, "taxConfirmed"),
|
||||
termsVersion: str(form, "termsVersion"),
|
||||
termsUrl: str(form, "termsUrl"),
|
||||
termsApproved: bool(form, "termsApproved"),
|
||||
travelLawChecked: bool(form, "travelLawChecked"),
|
||||
installmentsApproved: bool(form, "installmentsApproved"),
|
||||
installmentRuleApproved: bool(form, "installmentRuleApproved"),
|
||||
reservationMinutes: str(form, "reservationMinutes"),
|
||||
finalInstallmentLeadDays: str(form, "finalInstallmentLeadDays"),
|
||||
minInstallmentGapDays: str(form, "minInstallmentGapDays"),
|
||||
testPhaseOverride: str(form, "testPhaseOverride"),
|
||||
});
|
||||
if (!parsed.success) back("/admin/einstellungen", { ok: false, message: firstIssue(parsed.error) });
|
||||
const result = await updateCamp(getDb(), parsed.data, adminActor(admin));
|
||||
revalidatePath("/admin", "layout");
|
||||
revalidatePath("/");
|
||||
back("/admin/einstellungen", result);
|
||||
}
|
||||
Reference in new issue
Block a user