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 <noreply@anthropic.com>
This commit is contained in:
parent
34368e1dde
commit
f1bcc957f9
4 changed files with 37 additions and 6 deletions
|
|
@ -38,9 +38,6 @@ export default async function handler(req: any, res: any) {
|
||||||
client_secret: clientSecret,
|
client_secret: clientSecret,
|
||||||
grant_type: "authorization_code",
|
grant_type: "authorization_code",
|
||||||
code,
|
code,
|
||||||
redirect_uri:
|
|
||||||
process.env.DISCORD_ACTIVITY_REDIRECT_URI ||
|
|
||||||
"https://aethex.dev/activity",
|
|
||||||
}).toString(),
|
}).toString(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,22 @@ const ARMS: Arm[] = [
|
||||||
textColor: "text-purple-400",
|
textColor: "text-purple-400",
|
||||||
href: "/staff",
|
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('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><rect width="512" height="512" rx="96" fill="%23050505"/><polygon points="256,48 444,152 444,360 256,464 68,360 68,152" fill="none" stroke="%2300ffff" stroke-width="18" opacity="0.9"/><text x="256" y="320" text-anchor="middle" font-family="Orbitron,monospace" font-size="220" font-weight="700" fill="%2300ffff">Æ</text></svg>')}`;
|
||||||
|
|
||||||
const LOGO_URLS: Record<string, string> = {
|
const LOGO_URLS: Record<string, string> = {
|
||||||
|
studio: STUDIO_SVG,
|
||||||
staff:
|
staff:
|
||||||
"https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fc0414efd7af54ef4b821a05d469150d0?format=webp&width=800",
|
"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",
|
labs: "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fd93f7113d34347469e74421c3a3412e5?format=webp&width=800",
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ import blogIndexHandler from "../api/blog/index";
|
||||||
import blogSlugHandler from "../api/blog/[slug]";
|
import blogSlugHandler from "../api/blog/[slug]";
|
||||||
import aiChatHandler from "../api/ai/chat";
|
import aiChatHandler from "../api/ai/chat";
|
||||||
import aiTitleHandler from "../api/ai/title";
|
import aiTitleHandler from "../api/ai/title";
|
||||||
|
import createCheckoutHandler from "../api/subscriptions/create-checkout";
|
||||||
|
import manageSubscriptionHandler from "../api/subscriptions/manage";
|
||||||
|
|
||||||
// Developer API Keys handlers
|
// Developer API Keys handlers
|
||||||
import {
|
import {
|
||||||
|
|
@ -340,6 +342,11 @@ export function createServer() {
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
// Allow embedding in iframes (Discord Activities need this)
|
// Allow embedding in iframes (Discord Activities need this)
|
||||||
res.setHeader("X-Frame-Options", "ALLOWALL");
|
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
|
// Allow Discord to access the iframe
|
||||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
res.setHeader(
|
res.setHeader(
|
||||||
|
|
@ -1815,9 +1822,6 @@ export function createServer() {
|
||||||
client_secret: clientSecret,
|
client_secret: clientSecret,
|
||||||
grant_type: "authorization_code",
|
grant_type: "authorization_code",
|
||||||
code,
|
code,
|
||||||
redirect_uri:
|
|
||||||
process.env.DISCORD_ACTIVITY_REDIRECT_URI ||
|
|
||||||
"https://aethex.dev/activity",
|
|
||||||
}).toString(),
|
}).toString(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -8177,5 +8181,16 @@ export function createServer() {
|
||||||
app.post("/api/ai/chat", aiChatHandler);
|
app.post("/api/ai/chat", aiChatHandler);
|
||||||
app.post("/api/ai/title", aiTitleHandler);
|
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;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,12 @@ app.get("*", async (req, res) => {
|
||||||
return res.status(404).json({ error: "API endpoint not found" });
|
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 {
|
try {
|
||||||
const template = getTemplate();
|
const template = getTemplate();
|
||||||
const meta = await resolveRouteMeta(req.path);
|
const meta = await resolveRouteMeta(req.path);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue