From a68a2b9f8e92409bd1332ed6ef1c7868a8ac516a Mon Sep 17 00:00:00 2001 From: AeThex Date: Wed, 15 Apr 2026 01:59:56 +0000 Subject: [PATCH] fix: build client in Docker and serve compiled SPA from Express MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vite dev mode is incompatible with Discord Activity's iframe CSP — the HMR WebSocket is blocked which breaks the React JSX dev runtime (_jsxDEV). - Build client (vite build) during Docker image build so dist/spa/ exists - Add express.static serving dist/spa/ assets in server/index.ts - Add SPA catch-all to serve dist/spa/index.html for non-API routes The Activity now loads the production compiled bundle instead of Vite's dev-mode TypeScript modules, resolving the _jsxDEV crash. Co-Authored-By: Claude Sonnet 4.6 --- Dockerfile | 4 ++-- server/index.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0e7af90a..433b5bae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,8 +13,8 @@ RUN if [ -f pnpm-lock.yaml ]; then npm install -g pnpm && pnpm install --frozen- # Copy source code COPY . . -# Build the app (frontend + server) -# RUN npm run build +# Build the client so the Activity gets compiled JS (no Vite dev mode in Discord iframe) +RUN npm run build:client # Set environment ENV NODE_ENV=production diff --git a/server/index.ts b/server/index.ts index 99d20d9e..047f4f19 100644 --- a/server/index.ts +++ b/server/index.ts @@ -2,6 +2,7 @@ import "dotenv/config"; import "dotenv/config"; import express from "express"; import cors from "cors"; +import path from "path"; import { adminSupabase } from "./supabase"; import { emailService } from "./email"; import { randomUUID, createHash, createVerify, randomBytes, createHmac } from "crypto"; @@ -8192,5 +8193,17 @@ export function createServer() { return manageSubscriptionHandler(req as any, res as any); }); + // Serve compiled SPA static assets (built by `npm run build:client`) + const spaDir = path.join(process.cwd(), "dist/spa"); + app.use(express.static(spaDir, { index: false })); + + // SPA catch-all — serve index.html for all non-API routes + app.get("*", (req: express.Request, res: express.Response) => { + if (req.path.startsWith("/api/")) { + return res.status(404).json({ error: "API endpoint not found" }); + } + res.sendFile(path.join(spaDir, "index.html")); + }); + return app; }