| 1 | /**
|
|---|
| 2 | * Embeddable player (premium feature #7).
|
|---|
| 3 | *
|
|---|
| 4 | * GET /embed -> a standalone, compact audio player page (no shell),
|
|---|
| 5 | * intended to be placed in an <iframe> on EXTERNAL sites.
|
|---|
| 6 | *
|
|---|
| 7 | * The page is served by us (klonkt-origin), so audio requests from within
|
|---|
| 8 | * the iframe remain same-origin → the /audio/stream gate lets them through,
|
|---|
| 9 | * even when the iframe is on a foreign site. We only override Helmet's frameguard
|
|---|
| 10 | * + frame-ancestors so that external sites are allowed to embed us. Hub: /user/:slug/embed.
|
|---|
| 11 | */
|
|---|
| 12 |
|
|---|
| 13 | import express from 'express';
|
|---|
| 14 | import { premiumUnlocked } from '../services/PatreonService.js';
|
|---|
| 15 |
|
|---|
| 16 | const router = express.Router();
|
|---|
| 17 |
|
|---|
| 18 | router.get('/embed', (req, res, next) => {
|
|---|
| 19 | if (!premiumUnlocked()) return next();
|
|---|
| 20 | const site = res.locals.site;
|
|---|
| 21 | if (!site) return next();
|
|---|
| 22 |
|
|---|
| 23 | // Allow embedding on external sites (override the global frameguard/CSP).
|
|---|
| 24 | res.removeHeader('X-Frame-Options');
|
|---|
| 25 | res.setHeader(
|
|---|
| 26 | 'Content-Security-Policy',
|
|---|
| 27 | "default-src 'self'; media-src 'self' blob: https:; img-src 'self' data: https:; style-src 'unsafe-inline'; script-src 'unsafe-inline' 'self'; frame-ancestors *",
|
|---|
| 28 | );
|
|---|
| 29 |
|
|---|
| 30 | const tracks = (res.locals.audioTracks || []).map((t) => ({
|
|---|
| 31 | id: t.id, title: t.title, artist: t.artist, duration: t.duration, url: t.media_url,
|
|---|
| 32 | })).filter((t) => t.url);
|
|---|
| 33 |
|
|---|
| 34 | res.render('pages/embed-player', {
|
|---|
| 35 | site,
|
|---|
| 36 | embedTracks: tracks,
|
|---|
| 37 | siteUrlBase: res.locals.siteUrlBase || '',
|
|---|
| 38 | });
|
|---|
| 39 | });
|
|---|
| 40 |
|
|---|
| 41 | export default router;
|
|---|