feat(hetzner): update virtualcam with bypassing payments

This commit is contained in:
2026-08-22 14:46:22 +02:00
parent e64c3d1310
commit b80316267f
4 changed files with 40 additions and 60 deletions
@@ -1,29 +0,0 @@
--- a/src/lib/catalog.ts
+++ b/src/lib/catalog.ts
@@ -1,3 +1,8 @@
import { prisma } from "@/lib/db";
+
+// During `next build`, Next evaluates generateStaticParams/generateMetadata for
+// each route, which calls these DB readers. No database exists in the sandboxed
+// Nix build, so short-circuit them here; the live site fetches real rows/request.
+const BUILD = process.env.NEXT_PHASE === "phase-production-build";
export type TierWithFeatures = {
@@ -33,2 +38,3 @@
export async function getActiveTiers(): Promise<TierWithFeatures[]> {
+ if (BUILD) return [];
const tiers = await prisma.tier.findMany({
@@ -45,4 +51,5 @@
export async function getTierBySlug(
slug: string,
): Promise<TierWithFeatures | null> {
+ if (BUILD) return null;
const tier = await prisma.tier.findUnique({
@@ -58,2 +65,3 @@
export async function getDocPages() {
+ if (BUILD) return [];
return prisma.docPage.findMany({
@@ -65,2 +73,3 @@
export async function getDocBySlug(slug: string) {
+ if (BUILD) return null;
return prisma.docPage.findUnique({ where: { slug } });
@@ -0,0 +1,32 @@
--- a/src/app/api/checkout/route.ts
+++ b/src/app/api/checkout/route.ts
@@ -7,6 +7,8 @@
export const dynamic = "force-dynamic";
+const BYPASS_PAYMENTS = process.env.BYPASS_PAYMENTS === "true";
+
export async function POST(req: Request) {
// Each checkout hits the payment provider, so cap order creation per user
// and per IP to prevent order spam and provider-API abuse.
@@ -86,6 +88,20 @@
const paid = await prisma.order.findUnique({
where: { id: order.id },
include: { license: true },
+ });
+ return NextResponse.json({
+ ok: true,
+ orderId: order.id,
+ licenseKey: paid?.license?.key ?? null,
+ tier: tier.slug,
+ });
+ }
+
+ if (BYPASS_PAYMENTS) {
+ await markOrderPaid({ orderId: order.id, providerRef: "bypass-test" });
+ const paid = await prisma.order.findUnique({
+ where: { id: order.id },
+ include: { license: true },
});
return NextResponse.json({
ok: true,
@@ -1,30 +0,0 @@
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,19 +1,14 @@
import type { Metadata } from "next";
-import { Pixelify_Sans } from "next/font/google";
import "./globals.css";
import { getCurrentUser } from "@/lib/auth";
import { getAppUrl } from "@/lib/env";
import { Providers } from "@/components/providers";
import { AuthProvider } from "@/contexts/auth-context";
import { MatrixEffects } from "@/components/matrix-effects";
import { SiteHeader } from "@/components/site-header";
import { SiteFooter } from "@/components/site-footer";
-
-const pixelify = Pixelify_Sans({
- subsets: ["latin"],
- weight: ["400", "500", "600", "700"],
- variable: "--font-pixelify",
- display: "swap",
-});
-
+// Built fully dynamic so the sandboxed Nix build needs no database and no
+// external font download; also correct for a DB-driven storefront.
+export const dynamic = "force-dynamic";
+
export const metadata: Metadata = {
@@ -50,2 +45,2 @@
- <html lang="en" className={`${pixelify.variable} h-full`} suppressHydrationWarning>
+ <html lang="en" className="h-full" suppressHydrationWarning>
<body className="min-h-full flex flex-col bg-background text-foreground">
@@ -15,6 +15,12 @@
rev = rev;
};
srcPatched = pkgs.applyPatches {
name = "virtualcam-website-patched";
src = src;
patches = [./virtualcam-checkout.patch];
};
apiSrc = builtins.fetchGit {
url = "ssh://git@git.severijnse.eu:2222/jory/virtualcam-api.git";
rev = apiRev;
@@ -24,7 +30,7 @@
app = unstablePkgs.buildNpmPackage {
pname = "virtualcam-website";
version = "0.1.0";
src = src;
src = srcPatched;
npmDepsHash = "sha256-0g98Jh/RwoicjrfiSbfqNo331k3ab8hINjV6dHHN0y4=";
nodejs = unstablePkgs.nodejs;
@@ -232,6 +238,7 @@ in {
"DATABASE_URL=${dbUrl}"
"APP_URL=https://virtualcam.severijnse.eu"
"PAYMENTS_MODE=shkeeper"
"BYPASS_PAYMENTS=true"
"ADMIN_EMAILS=jory@severijnse.eu"
"SMTP_HOST=mail.severijnse.eu"
"SMTP_PORT=587"