From f1bcc957f913dca831fa18f58c2b9ec9c6cf80d3 Mon Sep 17 00:00:00 2001 From: AeThex Date: Tue, 14 Apr 2026 23:49:50 +0000 Subject: [PATCH] fix: Discord Activity token exchange, CSP headers, subscription routes, and static asset 404 - Remove redirect_uri from Discord token exchange (Activities use proxy auth, not redirect flow) - Add Content-Security-Policy with frame-ancestors for Discord embedding (was only in vercel.json) - Wire up subscription create-checkout and manage routes in Express - Add Studio arm to ArmSwitcher with external link - Prevent SPA catch-all from serving HTML for missing static assets (fixes script.js Unexpected token error) Co-Authored-By: Claude Sonnet 4.6 --- api/discord/token.ts | 3 --- client/components/ArmSwitcher.tsx | 13 +++++++++++++ server/index.ts | 21 ++++++++++++++++++--- server/node-build.ts | 6 ++++++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/api/discord/token.ts b/api/discord/token.ts index f3a3f95c..0cfbf7d8 100644 --- a/api/discord/token.ts +++ b/api/discord/token.ts @@ -38,9 +38,6 @@ export default async function handler(req: any, res: any) { client_secret: clientSecret, grant_type: "authorization_code", code, - redirect_uri: - process.env.DISCORD_ACTIVITY_REDIRECT_URI || - "https://aethex.dev/activity", }).toString(), }, ); diff --git a/client/components/ArmSwitcher.tsx b/client/components/ArmSwitcher.tsx index 4bac7d6f..30506c8b 100644 --- a/client/components/ArmSwitcher.tsx +++ b/client/components/ArmSwitcher.tsx @@ -68,9 +68,22 @@ const ARMS: Arm[] = [ textColor: "text-purple-400", href: "/staff", }, + { + id: "studio", + name: "AeThex | Studio", + label: "Studio", + color: "#00ffff", + bgColor: "bg-cyan-500/20", + textColor: "text-cyan-400", + href: "https://aethex.studio", + external: true, + }, ]; +const STUDIO_SVG = `data:image/svg+xml,${encodeURIComponent('Æ')}`; + const LOGO_URLS: Record = { + studio: STUDIO_SVG, staff: "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fc0414efd7af54ef4b821a05d469150d0?format=webp&width=800", labs: "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fd93f7113d34347469e74421c3a3412e5?format=webp&width=800", diff --git a/server/index.ts b/server/index.ts index 03591f93..99d20d9e 100644 --- a/server/index.ts +++ b/server/index.ts @@ -41,6 +41,8 @@ import blogIndexHandler from "../api/blog/index"; import blogSlugHandler from "../api/blog/[slug]"; import aiChatHandler from "../api/ai/chat"; import aiTitleHandler from "../api/ai/title"; +import createCheckoutHandler from "../api/subscriptions/create-checkout"; +import manageSubscriptionHandler from "../api/subscriptions/manage"; // Developer API Keys handlers import { @@ -340,6 +342,11 @@ export function createServer() { app.use((req, res, next) => { // Allow embedding in iframes (Discord Activities need this) res.setHeader("X-Frame-Options", "ALLOWALL"); + // CSP with frame-ancestors for Discord Activity embedding + res.setHeader( + "Content-Security-Policy", + "default-src 'self' https: data: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: blob: https:; font-src 'self' data: https:; connect-src 'self' https: wss:; frame-ancestors 'self' https://discord.com https://*.discord.com https://*.discordsays.com", + ); // Allow Discord to access the iframe res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader( @@ -1815,9 +1822,6 @@ export function createServer() { client_secret: clientSecret, grant_type: "authorization_code", code, - redirect_uri: - process.env.DISCORD_ACTIVITY_REDIRECT_URI || - "https://aethex.dev/activity", }).toString(), }, ); @@ -8177,5 +8181,16 @@ export function createServer() { app.post("/api/ai/chat", aiChatHandler); app.post("/api/ai/title", aiTitleHandler); + // Subscription API routes + app.post("/api/subscriptions/create-checkout", (req: express.Request, res: express.Response) => { + return createCheckoutHandler(req as any, res as any); + }); + app.get("/api/subscriptions/manage", (req: express.Request, res: express.Response) => { + return manageSubscriptionHandler(req as any, res as any); + }); + app.post("/api/subscriptions/manage", (req: express.Request, res: express.Response) => { + return manageSubscriptionHandler(req as any, res as any); + }); + return app; } diff --git a/server/node-build.ts b/server/node-build.ts index ca9a4d98..cae62dcd 100644 --- a/server/node-build.ts +++ b/server/node-build.ts @@ -335,6 +335,12 @@ app.get("*", async (req, res) => { return res.status(404).json({ error: "API endpoint not found" }); } + // Don't serve the SPA shell for missing static-asset requests — they should 404 + // cleanly rather than returning HTML (which causes "Unexpected token '<'" JS errors). + if (/\.(js|mjs|cjs|css|map|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|webp|avif)$/i.test(req.path)) { + return res.status(404).send("Not found"); + } + try { const template = getTemplate(); const meta = await resolveRouteMeta(req.path);