import * as plugins from '../plugins.js'; /** * Built-in routes controller for TypedServer * Handles robots.txt, manifest.json, sitemap, feed, appversion */ @plugins.smartserve.Route('') export class BuiltInRoutesController { private options: { domain?: string; robots?: boolean; manifest?: plugins.smartmanifest.SmartManifest; sitemap?: boolean; feed?: boolean; appVersion?: string; feedMetadata?: plugins.smartfeed.IFeedOptions; articleGetterFunction?: () => Promise; blockWaybackMachine?: boolean; getSitemapUrls: () => plugins.smartsitemap.IUrlInfo[]; }; constructor(options: typeof BuiltInRoutesController.prototype.options) { this.options = options; } @plugins.smartserve.Get('/robots.txt') async getRobots(ctx: plugins.smartserve.IRequestContext): Promise { if (!this.options.robots || !this.options.domain) { throw new plugins.smartserve.RouteNotFoundError(ctx.path, ctx.method); } const robotsContent = [ 'User-agent: *', 'Allow: /', `Sitemap: https://${this.options.domain}/sitemap`, ]; if (this.options.blockWaybackMachine) { robotsContent.push('', 'User-agent: ia_archiver', 'Disallow: /'); } return new Response(robotsContent.join('\n'), { status: 200, headers: { 'Content-Type': 'text/plain' }, }); } @plugins.smartserve.Get('/manifest.json') async getManifest(ctx: plugins.smartserve.IRequestContext): Promise { if (!this.options.manifest) { throw new plugins.smartserve.RouteNotFoundError(ctx.path, ctx.method); } return new Response(this.options.manifest.jsonString(), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } @plugins.smartserve.Get('/sitemap') async getSitemap(ctx: plugins.smartserve.IRequestContext): Promise { if (!this.options.sitemap || !this.options.domain) { throw new plugins.smartserve.RouteNotFoundError(ctx.path, ctx.method); } const smartsitemap = new plugins.smartsitemap.SmartSitemap(); const urls = this.options.getSitemapUrls(); const sitemapXml = await smartsitemap.createSitemapFromUrlInfoArray(urls); return new Response(sitemapXml, { status: 200, headers: { 'Content-Type': 'application/xml' }, }); } @plugins.smartserve.Get('/sitemap-news') async getSitemapNews(ctx: plugins.smartserve.IRequestContext): Promise { if (!this.options.sitemap || !this.options.domain || !this.options.articleGetterFunction) { throw new plugins.smartserve.RouteNotFoundError(ctx.path, ctx.method); } const smartsitemap = new plugins.smartsitemap.SmartSitemap(); const articles = await this.options.articleGetterFunction(); const sitemapNewsXml = await smartsitemap.createSitemapNewsFromArticleArray(articles); return new Response(sitemapNewsXml, { status: 200, headers: { 'Content-Type': 'application/xml' }, }); } @plugins.smartserve.Get('/feed') async getFeed(ctx: plugins.smartserve.IRequestContext): Promise { if (!this.options.feed || !this.options.feedMetadata) { throw new plugins.smartserve.RouteNotFoundError(ctx.path, ctx.method); } const smartfeed = new plugins.smartfeed.Smartfeed(); const articles = this.options.articleGetterFunction ? await this.options.articleGetterFunction() : []; const feedXml = await smartfeed.createFeedFromArticleArray( this.options.feedMetadata, articles ); return new Response(feedXml, { status: 200, headers: { 'Content-Type': 'application/atom+xml' }, }); } @plugins.smartserve.Get('/appversion') async getAppVersion(ctx: plugins.smartserve.IRequestContext): Promise { if (!this.options.appVersion) { throw new plugins.smartserve.RouteNotFoundError(ctx.path, ctx.method); } return new Response(this.options.appVersion, { status: 200, headers: { 'Content-Type': 'text/plain' }, }); } }