tag is the whole integration - the JS injects its own CSS * link. Short client cache so editors see fixes quickly; the widget is a * review tool, not a high-traffic asset. */ final class WidgetAssetMiddleware implements MiddlewareInterface { private const FILES = [ '/api/fe-comment/widget.js' => ['EXT:fe_comment/Resources/Public/JavaScript/fe-comment.js', 'application/javascript; charset=utf-8'], '/api/fe-comment/widget.css' => ['EXT:fe_comment/Resources/Public/Css/fe-comment.css', 'text/css; charset=utf-8'], ]; public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $path = $request->getUri()->getPath(); $file = self::FILES[$path] ?? null; if ($file === null) { return $handler->handle($request); } // Kill switch: the go-live off state serves an EMPTY file instead of // a 404, so a front end still embedding the script tag stays silent - // no console errors, no build change needed. if (!GeneralUtility::makeInstance(\Evomedien\FeComment\Service\ExtensionSettings::class)->enabled()) { return (new Response()) ->withHeader('Content-Type', $file[1]) ->withHeader('Cache-Control', 'public, max-age=300') ->withBody(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\StreamFactory::class) ->createStream('/* fe_comment is disabled (extension configuration) */')); } $absolute = GeneralUtility::getFileAbsFileName($file[0]); if ($absolute === '' || !is_file($absolute)) { return (new Response())->withStatus(404); } return (new Response()) ->withHeader('Content-Type', $file[1]) ->withHeader('Cache-Control', 'public, max-age=300') ->withBody(new Stream($absolute, 'rb')); } }