function assertRouter(api) { if (!api || typeof api.get !== 'function') { throw new Error('attachPortalSeoDiscoveryRoutes requires an Express-compatible router'); } } export function attachPortalSeoDiscoveryRoutes( app, { getAuthPool = () => null, getMindSpaceConfig = async () => ({ seoGeo: { enabled: false } }), getSeoDiscoveryService = () => null, resolveRequestOrigin = () => '', } = {}, ) { assertRouter(app); app.get('/robots.txt', async (req, res) => { const config = await getMindSpaceConfig(); const seoGeo = config?.seoGeo ?? {}; if (!seoGeo.enabled || !seoGeo.seo?.robotsTxt) { return res.type('text/plain; charset=utf-8').send('User-agent: *\nDisallow: /\n'); } const service = getSeoDiscoveryService(); if (!service) { return res.status(503).type('text/plain; charset=utf-8').send('SEO discovery unavailable\n'); } const origin = resolveRequestOrigin(req); return res .type('text/plain; charset=utf-8') .set('Cache-Control', 'public, max-age=300') .send( service.renderRobotsTxt({ origin, sitemapEnabled: Boolean(seoGeo.seo?.sitemap), llmsTxtEnabled: Boolean(seoGeo.geo?.llmsTxt), }), ); }); app.get('/sitemap.xml', async (req, res) => { const config = await getMindSpaceConfig(); const seoGeo = config?.seoGeo ?? {}; if (!seoGeo.enabled || !seoGeo.seo?.enabled || !seoGeo.seo?.sitemap) { return res.status(404).type('text/plain; charset=utf-8').send('Not Found\n'); } const pool = getAuthPool(); const service = getSeoDiscoveryService(); if (!pool || !service) { return res.status(503).type('text/plain; charset=utf-8').send('SEO discovery unavailable\n'); } const origin = resolveRequestOrigin(req); const entries = await service.listIndexablePublications(pool, { limit: req.query.limit, offset: req.query.offset, }); return res .type('application/xml; charset=utf-8') .set('Cache-Control', 'public, max-age=300') .send(service.renderSitemapXml(entries, { origin })); }); app.get('/llms.txt', async (req, res) => { const config = await getMindSpaceConfig(); const seoGeo = config?.seoGeo ?? {}; if (!seoGeo.enabled || !seoGeo.geo?.enabled || !seoGeo.geo?.llmsTxt) { return res.status(404).type('text/plain; charset=utf-8').send('Not Found\n'); } const pool = getAuthPool(); const service = getSeoDiscoveryService(); if (!pool || !service) { return res.status(503).type('text/plain; charset=utf-8').send('GEO discovery unavailable\n'); } const origin = resolveRequestOrigin(req); const entries = await service.listIndexablePublications(pool, { limit: req.query.limit, offset: req.query.offset, }); return res .type('text/plain; charset=utf-8') .set('Cache-Control', 'public, max-age=300') .send(service.renderLlmsTxt(entries, { origin })); }); }