Fix Discord interactions endpoint with proper raw body handling
cgen-3347352060874ae89711ff38495e64ab
This commit is contained in:
parent
d103af162d
commit
aaa9e3cd2c
1 changed files with 52 additions and 19 deletions
|
|
@ -1,10 +1,31 @@
|
||||||
import { VercelRequest, VercelResponse } from "@vercel/node";
|
import { VercelRequest, VercelResponse } from "@vercel/node";
|
||||||
import { createVerify } from "crypto";
|
import { createVerify } from "crypto";
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
api: {
|
||||||
|
bodyParser: {
|
||||||
|
raw: {
|
||||||
|
type: "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default function handler(req: VercelRequest, res: VercelResponse) {
|
export default function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
// Only accept POST requests
|
// Set CORS headers for Discord
|
||||||
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
||||||
|
res.setHeader(
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Content-Type, x-signature-ed25519, x-signature-timestamp"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (req.method === "OPTIONS") {
|
||||||
|
return res.status(200).end();
|
||||||
|
}
|
||||||
|
|
||||||
if (req.method !== "POST") {
|
if (req.method !== "POST") {
|
||||||
res.setHeader("Allow", "POST");
|
res.setHeader("Allow", "POST, OPTIONS");
|
||||||
return res.status(405).json({ error: "Method not allowed" });
|
return res.status(405).json({ error: "Method not allowed" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13,29 +34,33 @@ export default function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
const timestamp = req.headers["x-signature-timestamp"] as string;
|
const timestamp = req.headers["x-signature-timestamp"] as string;
|
||||||
const publicKey = process.env.DISCORD_PUBLIC_KEY;
|
const publicKey = process.env.DISCORD_PUBLIC_KEY;
|
||||||
|
|
||||||
console.log("[Discord] Interaction received at", new Date().toISOString());
|
console.log("[Discord] Interaction received");
|
||||||
|
console.log("[Discord] Has signature:", !!signature);
|
||||||
|
console.log("[Discord] Has timestamp:", !!timestamp);
|
||||||
|
console.log("[Discord] Has public key:", !!publicKey);
|
||||||
|
|
||||||
if (!publicKey) {
|
if (!publicKey) {
|
||||||
console.error("[Discord] DISCORD_PUBLIC_KEY not set");
|
console.error("[Discord] DISCORD_PUBLIC_KEY not set");
|
||||||
return res.status(401).json({ error: "Server not configured" });
|
return res.status(500).json({ error: "Server not configured" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!signature || !timestamp) {
|
if (!signature || !timestamp) {
|
||||||
console.error(
|
console.error("[Discord] Missing required headers");
|
||||||
"[Discord] Missing headers - signature:",
|
|
||||||
!!signature,
|
|
||||||
"timestamp:",
|
|
||||||
!!timestamp,
|
|
||||||
);
|
|
||||||
return res.status(401).json({ error: "Invalid request" });
|
return res.status(401).json({ error: "Invalid request" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get raw body
|
// Get raw body - Vercel sends it as Buffer with raw: true config
|
||||||
const rawBody =
|
const rawBody =
|
||||||
typeof req.body === "string" ? req.body : JSON.stringify(req.body);
|
req.body instanceof Buffer
|
||||||
|
? req.body.toString("utf8")
|
||||||
|
: typeof req.body === "string"
|
||||||
|
? req.body
|
||||||
|
: JSON.stringify(req.body);
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
const message = `${timestamp}${rawBody}`;
|
const message = `${timestamp}${rawBody}`;
|
||||||
|
console.log("[Discord] Verifying signature for message length:", message.length);
|
||||||
|
|
||||||
const signatureBuffer = Buffer.from(signature, "hex");
|
const signatureBuffer = Buffer.from(signature, "hex");
|
||||||
const verifier = createVerify("ed25519");
|
const verifier = createVerify("ed25519");
|
||||||
verifier.update(message);
|
verifier.update(message);
|
||||||
|
|
@ -46,23 +71,31 @@ export default function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
return res.status(401).json({ error: "Invalid signature" });
|
return res.status(401).json({ error: "Invalid signature" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const interaction =
|
console.log("[Discord] Signature verified successfully");
|
||||||
typeof req.body === "string" ? JSON.parse(req.body) : req.body;
|
|
||||||
console.log("[Discord] Valid interaction type:", interaction.type);
|
let interaction;
|
||||||
|
try {
|
||||||
|
interaction = typeof rawBody === "string" ? JSON.parse(rawBody) : rawBody;
|
||||||
|
} catch {
|
||||||
|
console.error("[Discord] Failed to parse JSON body");
|
||||||
|
return res.status(400).json({ error: "Invalid JSON" });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[Discord] Interaction type:", interaction?.type);
|
||||||
|
|
||||||
// Discord sends a PING to verify the endpoint
|
// Discord sends a PING to verify the endpoint
|
||||||
if (interaction.type === 1) {
|
if (interaction?.type === 1) {
|
||||||
console.log("[Discord] ✓ PING verified");
|
console.log("[Discord] ✓ PING verified");
|
||||||
return res.json({ type: 1 });
|
return res.status(200).json({ type: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all other interactions, acknowledge them
|
// For all other interactions, acknowledge them
|
||||||
return res.json({
|
return res.status(200).json({
|
||||||
type: 4,
|
type: 4,
|
||||||
data: { content: "Interaction received" },
|
data: { content: "Interaction received" },
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("[Discord] Error:", err?.message);
|
console.error("[Discord] Error:", err?.message, err?.stack);
|
||||||
return res.status(500).json({ error: "Server error" });
|
return res.status(500).json({ error: "Server error" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue