Files
nullresolve/ts/nullresolve.classes.nullresolve.ts
T

163 lines
5.3 KiB
TypeScript
Raw Normal View History

2024-12-26 00:29:13 +01:00
import * as plugins from './nullresolve.plugins.js';
2026-04-29 11:07:46 +00:00
import * as paths from './nullresolve.paths.js';
2024-12-26 00:29:13 +01:00
import { configObject } from './nullresolve.config.js';
2026-04-29 11:07:46 +00:00
import type { IHtmlInfoOptions } from '@api.global/typedserver/infohtml';
export interface INullResolveOptions {
port?: number;
serviceDomain?: string;
}
type TResolvedNullResolveOptions = INullResolveOptions & {
serviceDomain: string;
};
2024-12-26 00:29:13 +01:00
export class NullResolve {
2026-04-29 11:07:46 +00:00
public serviceServer: plugins.typedserver.utilityservers.UtilityServiceServer | null = null;
private options: TResolvedNullResolveOptions;
2024-12-26 00:29:13 +01:00
2026-04-29 11:07:46 +00:00
constructor(optionsArg: INullResolveOptions = {}) {
this.options = {
2024-12-26 00:29:13 +01:00
serviceDomain: 'nullresolve.lossless.one',
2026-04-29 11:07:46 +00:00
...optionsArg,
};
}
private async createInfoHtmlResponse(
infoHtmlOptionsArg: IHtmlInfoOptions,
) {
const { InfoHtml } = await import('@api.global/typedserver/infohtml');
const infoHtmlInstance = await InfoHtml.fromOptions(infoHtmlOptionsArg);
return new Response(infoHtmlInstance.htmlString, {
status: 200,
headers: {
'content-type': 'text/html; charset=utf-8',
},
});
}
2024-12-26 00:29:13 +01:00
2026-04-29 11:07:46 +00:00
private getStatusInfoOptions(statusCodeArg: string): IHtmlInfoOptions {
switch (statusCodeArg) {
case 'ipblock':
return {
title: 'Lossless Network: Blocked IP',
heading: 'Blocked IP',
text: 'Your IP (::CLIENT_IP::) is not allowed to access this resource.',
sentryDsn: configObject.sentryDsn,
sentryMessage: 'ipblock',
redirectTo: 'https://lossless.com',
};
case 'firewall':
return {
title: 'Lossless Network: Firewall',
heading: 'Firewall',
text: 'Your request has been blocked by our firewall since it showed possibly harmful behaviour.',
sentryDsn: configObject.sentryDsn,
sentryMessage: 'firewall',
redirectTo: 'https://lossless.com',
};
case '500class':
return {
title: 'Lossless Network: 5xx',
heading: '5xx',
text: '::CLOUDFLARE_ERROR_500S_BOX::',
sentryDsn: configObject.sentryDsn,
sentryMessage: '5xx error',
redirectTo: 'https://lossless.com',
};
case '1000class':
return {
title: 'Lossless Network: DNS Resolution failed',
heading: '1xxx',
text: '::CLOUDFLARE_ERROR_1000S_BOX::',
sentryDsn: configObject.sentryDsn,
sentryMessage: '1000 class error',
redirectTo: 'https://lossless.com',
};
case 'alwaysonline':
return {
title: 'Lossless Network: No Cache',
heading: 'No Cache',
text: '::ALWAYS_ONLINE_NO_COPY_BOX::',
sentryDsn: configObject.sentryDsn,
sentryMessage: 'alwaysonline triggered. Potentially offline!',
redirectTo: 'https://lossless.com',
};
case 'waf':
return {
title: 'Lossless Network: Firewall Challenge',
heading: 'Firewall Challenge',
text: '::CAPTCHA_BOX::',
redirectTo: 'https://lossless.com',
};
case 'country':
return {
title: 'Lossless Network: Country Challenge',
heading: 'Country Challenge',
text: '::CAPTCHA_BOX::',
redirectTo: 'https://lossless.com',
};
case 'attack':
return {
title: 'Lossless Network: Advanced User Challenge',
heading: 'Advanced User Challenge',
text: '::IM_UNDER_ATTACK_BOX::',
redirectTo: 'https://lossless.com',
};
default: {
const statusInstance = plugins.smartstatus.HttpStatus.getHttpStatusByString(statusCodeArg);
return {
title: `Lossless Network: ${statusInstance.code.toString()}`,
heading: statusInstance.code.toString(),
text: statusInstance.text,
};
}
}
}
2024-12-26 00:29:13 +01:00
2026-04-29 11:07:46 +00:00
private async addCustomRoutes(typedServerArg: plugins.typedserver.TypedServer) {
typedServerArg.addRoute('/status/:code', 'GET', async (ctxArg) => {
return this.createInfoHtmlResponse(this.getStatusInfoOptions(ctxArg.params.code));
});
2024-12-26 00:29:13 +01:00
2026-04-29 11:07:46 +00:00
typedServerArg.addRoute('/custom', 'GET', async (ctxArg) => {
const options = {
title: ctxArg.query.title || 'Lossless Network',
heading: ctxArg.query.heading || 'Error!',
text: ctxArg.query.text || 'Please wait...',
redirectTo: ctxArg.query.redirectTo || 'https://lossless.com',
};
return this.createInfoHtmlResponse({
...options,
sentryDsn: configObject.sentryDsn,
sentryMessage: `nullresolve custom: ${options.title}`,
});
2024-12-26 00:29:13 +01:00
});
}
public async start() {
2026-04-29 11:07:46 +00:00
if (this.serviceServer) {
return;
}
const projectinfo = await plugins.projectinfo.ProjectInfo.create(paths.packageDir);
this.serviceServer = new plugins.typedserver.utilityservers.UtilityServiceServer({
serviceDomain: this.options.serviceDomain,
serviceName: 'nullresolve',
serviceVersion: projectinfo.npm.version,
port: this.options.port,
addCustomRoutes: async (typedServerArg) => this.addCustomRoutes(typedServerArg),
});
2024-12-26 00:29:13 +01:00
await this.serviceServer.start();
}
public async stop() {
2026-04-29 11:07:46 +00:00
if (!this.serviceServer) {
return;
}
2024-12-26 00:29:13 +01:00
await this.serviceServer.stop();
2026-04-29 11:07:46 +00:00
this.serviceServer = null;
2024-12-26 00:29:13 +01:00
}
}