207 lines
6.8 KiB
TypeScript
207 lines
6.8 KiB
TypeScript
import * as plugins from '../plugins.js';
|
|
import * as paths from '../paths.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<plugins.tsclass.content.IArticle[]>;
|
|
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<Response> {
|
|
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<Response> {
|
|
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<Response> {
|
|
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<Response> {
|
|
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<Response> {
|
|
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<Response> {
|
|
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' },
|
|
});
|
|
}
|
|
|
|
@plugins.smartserve.Get('/sw-dash')
|
|
async getSwDash(ctx: plugins.smartserve.IRequestContext): Promise<Response> {
|
|
// Import shared HTML from interfaces
|
|
const { SW_DASH_HTML } = await import('../../dist_ts_interfaces/serviceworker.js');
|
|
return new Response(SW_DASH_HTML, {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'text/html' },
|
|
});
|
|
}
|
|
|
|
// SW-dash data routes - return empty/unavailable when SW isn't active
|
|
@plugins.smartserve.Get('/sw-dash/metrics')
|
|
async getSwDashMetrics(): Promise<Response> {
|
|
return new Response(JSON.stringify({ error: 'Service worker not active', data: null }), {
|
|
status: 503,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
@plugins.smartserve.Get('/sw-dash/resources')
|
|
async getSwDashResources(): Promise<Response> {
|
|
return new Response(JSON.stringify({ error: 'Service worker not active', resources: [], domains: [], contentTypes: [], resourceCount: 0 }), {
|
|
status: 503,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
@plugins.smartserve.Get('/sw-dash/events')
|
|
async getSwDashEvents(): Promise<Response> {
|
|
return new Response(JSON.stringify({ error: 'Service worker not active', events: [], total: 0 }), {
|
|
status: 503,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
@plugins.smartserve.Get('/sw-dash/events/count')
|
|
async getSwDashEventsCount(): Promise<Response> {
|
|
return new Response(JSON.stringify({ error: 'Service worker not active', count: 0 }), {
|
|
status: 503,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
@plugins.smartserve.Get('/sw-dash/cumulative-metrics')
|
|
async getSwDashCumulativeMetrics(): Promise<Response> {
|
|
return new Response(JSON.stringify({ error: 'Service worker not active', data: null }), {
|
|
status: 503,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
@plugins.smartserve.Get('/sw-dash/speedtest')
|
|
async getSwDashSpeedtest(): Promise<Response> {
|
|
return new Response(JSON.stringify({ error: 'Service worker not active - speedtest unavailable' }), {
|
|
status: 503,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
@plugins.smartserve.Get('/sw-dash/bundle.js')
|
|
async getSwDashBundle(ctx: plugins.smartserve.IRequestContext): Promise<Response> {
|
|
try {
|
|
const bundleContent = (await plugins.fsInstance
|
|
.file(paths.swdashBundlePath)
|
|
.encoding('utf8')
|
|
.read()) as string;
|
|
|
|
return new Response(bundleContent, {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': 'text/javascript',
|
|
'Cache-Control': 'no-cache',
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to serve sw-dash bundle:', error);
|
|
return new Response('SW-Dash bundle not found', { status: 404 });
|
|
}
|
|
}
|
|
}
|