Add player bug reports and profile identity features

This commit is contained in:
2026-10-08 18:27:20 +05:00
parent 36e8d11eb4
commit 0b08672f4a
51 changed files with 3938 additions and 45 deletions

View File

@@ -0,0 +1,90 @@
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { admin, required } from "@/lib/supabase";
import { verifySupportWebhook } from "@/lib/support-signature";
import { supportCommand } from "@/shared/bug-reports";
export const runtime = "nodejs";
export async function POST(request: NextRequest) {
const secret = process.env.LINEAR_WEBHOOK_SECRET;
if (!secret)
return NextResponse.json(
{ error: "Support integration is not configured" },
{ status: 503 },
);
const reader = request.body?.getReader();
if (!reader) return new NextResponse(null, { status: 400 });
const chunks: Uint8Array[] = [];
let size = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
size += value.length;
if (size > 256 * 1024) {
await reader.cancel();
return new NextResponse(null, { status: 413 });
}
chunks.push(value);
}
const raw = Buffer.concat(chunks).toString("utf8");
if (
!verifySupportWebhook(raw, request.headers.get("linear-signature"), secret)
)
return new NextResponse(null, { status: 401 });
let decoded: unknown;
try {
decoded = JSON.parse(raw);
} catch {
return new NextResponse(null, { status: 400 });
}
const payload = z
.object({
action: z.string(),
type: z.string(),
actor: z
.object({
id: z.string(),
type: z.string(),
name: z.string().optional(),
})
.nullable()
.optional(),
data: z.object({
id: z.string().uuid(),
issueId: z.string().uuid().optional(),
body: z.string().optional(),
userId: z.string().optional(),
}),
})
.safeParse(decoded);
if (!payload.success) return new NextResponse(null, { status: 400 });
const event = payload.data;
// Only explicit new staff comments are public. Internal comments and user echoes stay private.
if (
event.action !== "create" ||
event.type !== "Comment" ||
event.actor?.type.toLowerCase() !== "user" ||
event.data.userId !== event.actor.id ||
!event.data.issueId
)
return NextResponse.json({ ok: true });
const command = supportCommand(event.data.body ?? "");
if (!command) return NextResponse.json({ ok: true });
try {
required(
await admin().rpc("receive_bug_reply", {
p_report: event.data.issueId,
p_external: event.data.id,
p_author: event.actor.name ?? "Support",
p_body: command.kind === "reply" ? command.text : "",
p_glow: command.kind === "glow" ? command.enabled : null,
}),
);
return NextResponse.json({ ok: true });
} catch {
return NextResponse.json(
{ error: "Support delivery failed" },
{ status: 503 },
);
}
}