Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
8fe5b6985c | |||
72e02bd611 | |||
fb7c1242a9 | |||
360766d8b4 | |||
9968dda0fa | |||
77b9e41bdb | |||
8bea58b434 | |||
bd9397eb13 | |||
6e7316d2b1 | |||
cba65bfb81 | |||
f06f25b4db | |||
316625c41b | |||
ee67c68c17 | |||
8fb2d8b3e8 | |||
75c89b040b | |||
b6d0843e3e | |||
1c5e2845d1 | |||
7798bf7e0a | |||
76db7d1733 | |||
1db472ab01 | |||
23e88030be | |||
1644cbbfad | |||
84e214d087 | |||
0bb7e438d5 |
16
package.json
16
package.json
@ -1,18 +1,20 @@
|
||||
{
|
||||
"name": "@api.global/typedserver",
|
||||
"version": "3.0.30",
|
||||
"version": "3.0.42",
|
||||
"description": "A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./dist_ts/index.js",
|
||||
"./ts": "./dist_ts/index.js",
|
||||
"./ts_edgeworker": "./dist_ts_edgeworker/index.js",
|
||||
"./ts_web_inject": "./dist_ts_web_inject/index.js",
|
||||
"./ts_web_serviceworker": "./dist_web_serviceworker",
|
||||
"./ts_web_serviceworker_client": "./dist_web_serviceworker_client"
|
||||
"./ts_web_serviceworker": "./dist_ts_web_serviceworker/index.js",
|
||||
"./ts_web_serviceworker_client": "./dist_ts_web_serviceworker_client/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run build && tstest test/",
|
||||
"build": "tsbuild tsfolders --web --allowimplicitany --skiplibcheck && tsbundle --from ./ts_web/index.ts --to ./dist_ts_web/bundle.js",
|
||||
"build": "tsbuild tsfolders --web --allowimplicitany && npm run bundle",
|
||||
"bundle": "tsbundle --from ./ts_web_inject/index.ts --to ./dist_ts_web_inject/bundle.js && tsbundle --from ./ts_web_serviceworker/index.ts --to ./dist_ts_web_serviceworker/serviceworker.bundle.js",
|
||||
"interfaces": "tsbuild interfaces --web --allowimplicitany --skiplibcheck",
|
||||
"docs": "tsdoc aidoc"
|
||||
},
|
||||
@ -58,6 +60,7 @@
|
||||
"@api.global/typedrequest": "^3.0.23",
|
||||
"@api.global/typedrequest-interfaces": "^3.0.19",
|
||||
"@api.global/typedsocket": "^3.0.1",
|
||||
"@cloudflare/workers-types": "^4.20240512.0",
|
||||
"@design.estate/dees-comms": "^1.0.24",
|
||||
"@push.rocks/lik": "^6.0.15",
|
||||
"@push.rocks/smartchok": "^1.0.34",
|
||||
@ -68,8 +71,11 @@
|
||||
"@push.rocks/smartjson": "^5.0.19",
|
||||
"@push.rocks/smartlog": "^3.0.3",
|
||||
"@push.rocks/smartlog-destination-devtools": "^1.0.10",
|
||||
"@push.rocks/smartlog-interfaces": "^3.0.0",
|
||||
"@push.rocks/smartmanifest": "^2.0.2",
|
||||
"@push.rocks/smartmatch": "^2.0.0",
|
||||
"@push.rocks/smartmime": "^1.0.5",
|
||||
"@push.rocks/smartntml": "^2.0.4",
|
||||
"@push.rocks/smartopen": "^2.0.0",
|
||||
"@push.rocks/smartpath": "^5.0.18",
|
||||
"@push.rocks/smartpromise": "^4.0.2",
|
||||
@ -90,7 +96,7 @@
|
||||
"lit": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.1.75",
|
||||
"@git.zone/tsbuild": "^2.1.76",
|
||||
"@git.zone/tsbundle": "^2.0.15",
|
||||
"@git.zone/tsrun": "^1.2.44",
|
||||
"@git.zone/tstest": "^1.0.90",
|
||||
|
6921
pnpm-lock.yaml
generated
6921
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@api.global/typedserver',
|
||||
version: '3.0.30',
|
||||
version: '3.0.42',
|
||||
description: 'A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.'
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as paths from './typedserver.paths.js';
|
||||
import * as paths from './paths.js';
|
||||
import * as interfaces from '../dist_ts_interfaces/index.js';
|
||||
import * as servertools from './servertools/index.js';
|
||||
import { type TCompressionMethod } from './servertools/classes.compressor.js';
|
||||
@ -103,7 +103,7 @@ export class TypedServer {
|
||||
case 'devtools':
|
||||
res.setHeader('Content-Type', 'text/javascript');
|
||||
res.status(200);
|
||||
res.write(plugins.smartfile.fs.toStringSync(paths.bundlePath));
|
||||
res.write(plugins.smartfile.fs.toStringSync(paths.injectBundlePath));
|
||||
res.end();
|
||||
break;
|
||||
case 'reloadcheck':
|
@ -4,7 +4,12 @@ import * as servertools from './servertools/index.js';
|
||||
|
||||
export { servertools };
|
||||
|
||||
export * from './typedserver.classes.typedserver.js';
|
||||
export * from './classes.typedserver.js';
|
||||
// Type helpers
|
||||
export type Request = plugins.express.Request;
|
||||
export type Response = plugins.express.Response;
|
||||
|
||||
|
||||
// lets export utilityservers
|
||||
import * as utilityservers from './utilityservers/index.js';
|
||||
export { utilityservers };
|
||||
|
8
ts/infohtml/00_commitinfo_data.ts
Normal file
8
ts/infohtml/00_commitinfo_data.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @pushrocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@losslessone_private/lole-infohtml',
|
||||
version: '1.0.39',
|
||||
description: 'html for displaying infos at lossless'
|
||||
}
|
44
ts/infohtml/index.ts
Normal file
44
ts/infohtml/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import * as plugins from './infohtml.plugins.js';
|
||||
|
||||
import { simpleInfo } from './template.js';
|
||||
|
||||
export interface IHtmlInfoOptions {
|
||||
text: string;
|
||||
heading?: string;
|
||||
title?: string;
|
||||
sentryMessage?: string;
|
||||
sentryDsn?: string;
|
||||
redirectTo?: string;
|
||||
}
|
||||
|
||||
export class InfoHtml {
|
||||
// STATIC
|
||||
public static async fromSimpleText(textArg: string) {
|
||||
const infohtmlInstance = new InfoHtml({
|
||||
text: textArg,
|
||||
heading: null,
|
||||
});
|
||||
await infohtmlInstance.init();
|
||||
return infohtmlInstance;
|
||||
}
|
||||
|
||||
public static async fromOptions(optionsArg: IHtmlInfoOptions) {
|
||||
const infohtmlInstance = new InfoHtml(optionsArg);
|
||||
await infohtmlInstance.init();
|
||||
return infohtmlInstance;
|
||||
}
|
||||
|
||||
// INSTANCE
|
||||
public options: IHtmlInfoOptions;
|
||||
public smartntmlInstance: plugins.smartntml.Smartntml;
|
||||
public htmlString: string;
|
||||
constructor(optionsArg: IHtmlInfoOptions) {
|
||||
this.options = optionsArg;
|
||||
}
|
||||
|
||||
public async init() {
|
||||
this.smartntmlInstance = new plugins.smartntml.Smartntml();
|
||||
this.htmlString = await simpleInfo(this.smartntmlInstance, this.options);
|
||||
return this.htmlString;
|
||||
}
|
||||
}
|
3
ts/infohtml/infohtml.plugins.ts
Normal file
3
ts/infohtml/infohtml.plugins.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import * as smartntml from '@push.rocks/smartntml';
|
||||
|
||||
export { smartntml };
|
160
ts/infohtml/template.ts
Normal file
160
ts/infohtml/template.ts
Normal file
@ -0,0 +1,160 @@
|
||||
import * as plugins from './infohtml.plugins.js';
|
||||
import { type IHtmlInfoOptions } from './index.js';
|
||||
|
||||
export const simpleInfo = async (
|
||||
smartntmlInstanceArg: plugins.smartntml.Smartntml,
|
||||
optionsArg: IHtmlInfoOptions
|
||||
) => {
|
||||
const html = plugins.smartntml.deesElement.html;
|
||||
const htmlTemplate = await plugins.smartntml.deesElement.html`
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>${optionsArg.title}</title>
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
const redirectUrl = '${optionsArg.redirectTo}';
|
||||
if (redirectUrl) {
|
||||
window.location = redirectUrl;
|
||||
}
|
||||
}, 5000);
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0px;
|
||||
background: #000000;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
min-height: 100vh;
|
||||
min-width: 100vw;
|
||||
border: 1px solid #e4002b;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 150px;
|
||||
padding-top: 70px;
|
||||
margin: 0px auto 30px auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.content .maintext {
|
||||
margin: 10px;
|
||||
color: #ffffff;
|
||||
background: #333;
|
||||
display: block;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content .maintext h1 {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.content .addontext {
|
||||
margin: 10px;
|
||||
color: #ffffff;
|
||||
background: #222;
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.content .text h1 {
|
||||
margin: 0px;
|
||||
font-weight: 100;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.content .text ul {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.legal {
|
||||
color: #fff;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"
|
||||
/>
|
||||
<script
|
||||
src="https://browser.sentry-cdn.com/5.4.0/bundle.min.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script>
|
||||
if (optionsArg.sentryDsn && optionsArg.sentryMessage) {
|
||||
Sentry.init({
|
||||
dsn: '${optionsArg.sentryDsn}',
|
||||
// ...
|
||||
});
|
||||
Sentry.setExtra('location', window.location.href);
|
||||
Sentry.captureMessage('${optionsArg.sentryMessage} @ ' + window.location.host);
|
||||
}
|
||||
</script>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="logo">
|
||||
<img src="https://assetbroker.lossless.one/brandfiles/lossless/svg-minimal-bright.svg" />
|
||||
</div>
|
||||
<div class="content">
|
||||
${(() => {
|
||||
const returnArray: plugins.smartntml.deesElement.TemplateResult[] = [];
|
||||
if (optionsArg.heading) {
|
||||
returnArray.push(html`
|
||||
<div class="maintext">
|
||||
<h1>${optionsArg.heading}</h1>
|
||||
${optionsArg.text}
|
||||
</div>
|
||||
`);
|
||||
} else {
|
||||
returnArray.push(html` <div class="maintext">${optionsArg.text}</div> `);
|
||||
}
|
||||
if (optionsArg.sentryDsn && optionsArg.sentryMessage) {
|
||||
returnArray.push(
|
||||
html`<div class="addontext">
|
||||
We recorded this event. Should you continue to see this page against your
|
||||
expectations, feel free to mail us at
|
||||
<a href="mailto:hello@lossless.com">hello@lossless.com</a>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
if (optionsArg.redirectTo) {
|
||||
returnArray.push(
|
||||
html`<div class="addontext">
|
||||
We will redirect you to ${optionsArg.redirectTo} in a few seconds.
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
return returnArray;
|
||||
})()}
|
||||
</div>
|
||||
<div class="legal">
|
||||
<a href="https://lossless.com">Lossless GmbH</a> / © 2014-${new Date().getFullYear()}
|
||||
/ <a href="https://lossless.gmbh">Legal Info</a> /
|
||||
<a href="https://lossless.gmbh">Privacy Policy</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
return smartntmlInstanceArg.renderTemplateResult(htmlTemplate);
|
||||
};
|
@ -4,4 +4,8 @@ export const packageDir = plugins.path.join(
|
||||
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
||||
'../'
|
||||
);
|
||||
export const distBundleDir = plugins.path.join(packageDir, './dist_bundle');
|
||||
|
||||
export const injectBundleDir = plugins.path.join(packageDir, './dist_ts_web_inject');
|
||||
export const injectBundlePath = plugins.path.join(injectBundleDir, './bundle.js');
|
||||
|
||||
export const serviceworkerBundleDir = plugins.path.join(packageDir, './dist_ts_web_serviceworker');
|
@ -9,7 +9,7 @@ import { setupRobots } from './tools.robots.js';
|
||||
import { setupManifest } from './tools.manifest.js';
|
||||
import { Sitemap } from './classes.sitemap.js';
|
||||
import { Feed } from './classes.feed.js';
|
||||
import { type IServerOptions } from '../typedserver.classes.typedserver.js';
|
||||
import { type IServerOptions } from '../classes.typedserver.js';
|
||||
export type TServerStatus = 'initiated' | 'running' | 'stopped';
|
||||
|
||||
/**
|
||||
|
@ -4,3 +4,9 @@ export * from './classes.handler.js';
|
||||
export * from './classes.handlerstatic.js';
|
||||
export * from './classes.handlerproxy.js';
|
||||
export * from './classes.handlertypedrouter.js';
|
||||
export * from './classes.compressor.js';
|
||||
import * as serviceworker from './tools.serviceworker.js';
|
||||
|
||||
export {
|
||||
serviceworker,
|
||||
}
|
||||
|
@ -3,28 +3,28 @@ import * as paths from '../paths.js';
|
||||
|
||||
import * as interfaces from '../../dist_ts_interfaces/index.js'
|
||||
import { Handler } from './classes.handler.js';
|
||||
import type { TypedServer } from '../typedserver.classes.typedserver.js';
|
||||
import type { TypedServer } from '../classes.typedserver.js';
|
||||
import { HandlerTypedRouter } from './classes.handlertypedrouter.js';
|
||||
|
||||
const lswJS: string = plugins.smartfile.fs.toStringSync(
|
||||
plugins.path.join(paths.distBundleDir, './lsw.js')
|
||||
const swBundleJs: string = plugins.smartfile.fs.toStringSync(
|
||||
plugins.path.join(paths.serviceworkerBundleDir, './serviceworker.bundle.js')
|
||||
);
|
||||
const lswJSMeta: string = plugins.smartfile.fs.toStringSync(
|
||||
plugins.path.join(paths.distBundleDir, './lsw.js.map')
|
||||
const swBundleJsMap: string = plugins.smartfile.fs.toStringSync(
|
||||
plugins.path.join(paths.serviceworkerBundleDir, './serviceworker.bundle.js.map')
|
||||
);
|
||||
let lswVersionInfo: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'] =
|
||||
let swVersionInfo: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'] =
|
||||
null;
|
||||
const serviceworkerHandler = new Handler(
|
||||
'GET',
|
||||
async (req, res) => {
|
||||
if (req.path === '/lsw.js') {
|
||||
if (req.path === '/serviceworker.bundle.js') {
|
||||
res.status(200);
|
||||
res.set('Content-Type', 'text/javascript');
|
||||
res.write(lswJS + '\n' + `/** appSemVer: ${lswVersionInfo?.appSemVer || 'not set'} */`);
|
||||
} else if (req.path === '/lsw.js.map') {
|
||||
res.write(swBundleJs + '\n' + `/** appSemVer: ${swVersionInfo?.appSemVer || 'not set'} */`);
|
||||
} else if (req.path === '/serviceworker.bundle.js.map') {
|
||||
res.status(200);
|
||||
res.set('Content-Type', 'application/json');
|
||||
res.write(lswJSMeta);
|
||||
res.write(swBundleJsMap);
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
@ -32,13 +32,13 @@ const serviceworkerHandler = new Handler(
|
||||
|
||||
export const addServiceWorkerRoute = (
|
||||
typedserverInstance: TypedServer,
|
||||
lswData: () => interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response']
|
||||
swDataFunc: () => interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response']
|
||||
) => {
|
||||
// lets the version info as unique string;
|
||||
lswVersionInfo = lswData();
|
||||
swVersionInfo = swDataFunc();
|
||||
|
||||
// the basic stuff
|
||||
typedserverInstance.server.addRoute('/lsw.js*', serviceworkerHandler);
|
||||
typedserverInstance.server.addRoute('/serviceworker.js*', serviceworkerHandler);
|
||||
|
||||
// the typed stuff
|
||||
const typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
@ -47,14 +47,14 @@ export const addServiceWorkerRoute = (
|
||||
new plugins.typedrequest.TypedHandler<interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo>(
|
||||
'serviceworker_versionInfo',
|
||||
async (req) => {
|
||||
const versionInfoResponse = lswData();
|
||||
const versionInfoResponse = swDataFunc();
|
||||
return versionInfoResponse;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
typedserverInstance.server.addRoute(
|
||||
'/lsw-typedrequest',
|
||||
'/sw-typedrequest',
|
||||
new HandlerTypedRouter(typedrouter)
|
||||
);
|
||||
};
|
||||
|
@ -1,8 +0,0 @@
|
||||
import * as plugins from './plugins.js';
|
||||
|
||||
export const packageDir = plugins.path.join(
|
||||
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
||||
'../'
|
||||
);
|
||||
|
||||
export const bundlePath = plugins.path.join(packageDir, './dist_ts_web/bundle.js');
|
51
ts/utilityservers/classes.serviceserver.ts
Normal file
51
ts/utilityservers/classes.serviceserver.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { TypedServer } from '../classes.typedserver.js';
|
||||
import * as servertools from '../servertools/index.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
|
||||
export interface ILoleServiceServerConstructorOptions {
|
||||
addCustomRoutes?: (serverArg: servertools.Server) => Promise<any>;
|
||||
serviceName: string;
|
||||
serviceVersion: string;
|
||||
serviceDomain: string;
|
||||
port?: number;
|
||||
}
|
||||
|
||||
// the main service server
|
||||
export class UtilityServiceServer {
|
||||
public options: ILoleServiceServerConstructorOptions;
|
||||
public typedServer: TypedServer;
|
||||
|
||||
constructor(optionsArg: ILoleServiceServerConstructorOptions) {
|
||||
this.options = optionsArg;
|
||||
}
|
||||
|
||||
public async start() {
|
||||
console.log('starting lole-serviceserver...')
|
||||
this.typedServer = new TypedServer({
|
||||
cors: true,
|
||||
domain: this.options.serviceDomain,
|
||||
forceSsl: false,
|
||||
port: this.options.port || 3000,
|
||||
robots: true,
|
||||
defaultAnswer: async () => {
|
||||
const InfoHtml = (await import('../infohtml/index.js')).InfoHtml;
|
||||
return (
|
||||
await InfoHtml.fromSimpleText(
|
||||
`${this.options.serviceName} (version ${this.options.serviceVersion})`
|
||||
)
|
||||
).htmlString;
|
||||
},
|
||||
});
|
||||
|
||||
// lets add any custom routes
|
||||
if (this.options.addCustomRoutes) {
|
||||
await this.options.addCustomRoutes(this.typedServer.server);
|
||||
}
|
||||
|
||||
await this.typedServer.start();
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
await this.typedServer.stop();
|
||||
}
|
||||
}
|
137
ts/utilityservers/classes.websiteserver.ts
Normal file
137
ts/utilityservers/classes.websiteserver.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
||||
import { type IServerOptions, TypedServer } from '../classes.typedserver.js';
|
||||
import type { Request, Response } from '../index.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import * as servertools from '../servertools/index.js';
|
||||
|
||||
export interface IUtilityWebsiteServerConstructorOptions {
|
||||
addCustomRoutes?: (serverArg: servertools.Server) => Promise<any>;
|
||||
appSemVer?: string;
|
||||
domain: string;
|
||||
serveDir: string;
|
||||
feedMetadata: IServerOptions['feedMetadata'];
|
||||
}
|
||||
|
||||
/**
|
||||
* the utility website server implements a best practice server for websites
|
||||
* It supports:
|
||||
* * live reload
|
||||
* * compression
|
||||
* * serviceworker
|
||||
* * pwa manifest
|
||||
*/
|
||||
export class UtilityWebsiteServer {
|
||||
public options: IUtilityWebsiteServerConstructorOptions;
|
||||
public typedserver: TypedServer;
|
||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
|
||||
constructor(optionsArg: IUtilityWebsiteServerConstructorOptions) {
|
||||
this.options = optionsArg;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public async start(portArg = 3000) {
|
||||
this.typedserver = new TypedServer({
|
||||
cors: true,
|
||||
injectReload: true,
|
||||
watch: true,
|
||||
serveDir: this.options.serveDir,
|
||||
enableCompression: true,
|
||||
preferredCompressionMethod: 'gzip',
|
||||
domain: this.options.domain,
|
||||
forceSsl: false,
|
||||
manifest: {
|
||||
name: this.options.domain,
|
||||
short_name: this.options.domain,
|
||||
start_url: '/',
|
||||
display_override: ['window-controls-overlay'],
|
||||
lang: 'en',
|
||||
background_color: '#000000',
|
||||
scope: '/',
|
||||
},
|
||||
port: portArg,
|
||||
|
||||
// features
|
||||
robots: true,
|
||||
sitemap: true,
|
||||
});
|
||||
|
||||
let lswData: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'] =
|
||||
{
|
||||
appHash: 'xxxxxx',
|
||||
appSemVer: this.options.appSemVer || 'x.x.x',
|
||||
};
|
||||
|
||||
// -> /lsw* - anything regarding serviceworker
|
||||
servertools.serviceworker.addServiceWorkerRoute(this.typedserver, () => {
|
||||
return lswData;
|
||||
});
|
||||
|
||||
// lets add ads.txt
|
||||
this.typedserver.server.addRoute(
|
||||
'/ads.txt',
|
||||
new servertools.Handler('GET', async (req, res) => {
|
||||
res.type('txt/plain');
|
||||
const adsTxt =
|
||||
['google.com, pub-4104137977476459, DIRECT, f08c47fec0942fa0'].join('\n') + '\n';
|
||||
res.write(adsTxt);
|
||||
res.end();
|
||||
})
|
||||
);
|
||||
|
||||
this.typedserver.server.addRoute(
|
||||
'/assetbroker/manifest/:manifestAsset',
|
||||
new servertools.Handler('GET', async (req, res) => {
|
||||
let manifestAssetName = req.params.manifestAsset;
|
||||
if (manifestAssetName === 'favicon.png') {
|
||||
manifestAssetName = `favicon_${this.options.domain
|
||||
.replace('.', '')
|
||||
.replace('losslesscom', 'lossless')}@2x_transparent.png`;
|
||||
}
|
||||
const fullOriginAssetUrl = `https://assetbroker.lossless.one/brandfiles/00general/${manifestAssetName}`;
|
||||
console.log(`Getting ${manifestAssetName} from ${fullOriginAssetUrl}`);
|
||||
const dataBuffer: Buffer = (await plugins.smartrequest.getBinary(fullOriginAssetUrl)).body;
|
||||
res.type('.png');
|
||||
res.write(dataBuffer);
|
||||
res.end();
|
||||
})
|
||||
);
|
||||
|
||||
// lets add any custom routes
|
||||
if (this.options.addCustomRoutes) {
|
||||
await this.options.addCustomRoutes(this.typedserver.server);
|
||||
}
|
||||
|
||||
// -> /* - serve the files
|
||||
this.typedserver.serveDirHashSubject.subscribe((appHash: string) => {
|
||||
lswData = {
|
||||
appHash,
|
||||
appSemVer: '1.0.0',
|
||||
};
|
||||
});
|
||||
|
||||
// lets setup the typedrouter chain
|
||||
this.typedserver.typedrouter.addTypedRouter(this.typedrouter);
|
||||
|
||||
// lets start everything
|
||||
console.log('routes are all set. Startin up now!');
|
||||
await this.typedserver.start();
|
||||
console.log('typedserver started!');
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
await this.typedserver.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* allows you to hanlde requests from other server instances without the need to listen for yourself
|
||||
* note smartexpress allows you start the instance wuith passing >>false<< as second parameter to .start();
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async handleRequest(req: Request, res: Response) {
|
||||
await this.typedserver.server.handleReqRes(req, res);
|
||||
}
|
||||
}
|
2
ts/utilityservers/index.ts
Normal file
2
ts/utilityservers/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './classes.serviceserver.js';
|
||||
export * from './classes.websiteserver.js';
|
8
ts_edgeworker/00_commitinfo_data.ts
Normal file
8
ts_edgeworker/00_commitinfo_data.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @pushrocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: 'cloudflare-workers',
|
||||
version: '1.0.192',
|
||||
description: 'cloudflare-workers'
|
||||
}
|
83
ts_edgeworker/analytics/analyzer.ts
Normal file
83
ts_edgeworker/analytics/analyzer.ts
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
import type { EdgeWorker } from '../classes.edgeworker.js';
|
||||
import type { WorkerEvent } from '../classes.workerevent.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import { SmartlogDestination } from './smartlog.js';
|
||||
|
||||
export interface IAnalyticsData {
|
||||
requestAgent: string;
|
||||
requestUrl: string;
|
||||
requestMethod: string;
|
||||
requestStartTime: number;
|
||||
responseStatus: number;
|
||||
responseEndTime: number;
|
||||
}
|
||||
|
||||
export class Analyzer {
|
||||
cworkerEventRef: WorkerEvent;
|
||||
|
||||
public data: IAnalyticsData = {
|
||||
requestAgent: 'unknown',
|
||||
requestMethod: 'unknown',
|
||||
requestUrl: 'unknown',
|
||||
requestStartTime: 0,
|
||||
responseStatus: 0,
|
||||
responseEndTime: 0,
|
||||
};
|
||||
|
||||
public finishedDeferred = plugins.smartpromise.defer();
|
||||
|
||||
constructor(cworkerEventRefArg: WorkerEvent) {
|
||||
this.cworkerEventRef = cworkerEventRefArg;
|
||||
this.smartlog.addLogDestination(new SmartlogDestination(this.cworkerEventRef.options.edgeWorkerRef));
|
||||
}
|
||||
public smartlog = new plugins.smartlog.Smartlog({
|
||||
logContext: {
|
||||
environment: 'production',
|
||||
runtime: "cloudflare_workers",
|
||||
zone: 'servezone',
|
||||
company: 'Lossless GmbH',
|
||||
companyunit: 'Lossless Cloud',
|
||||
containerName: 'cloudflare_workers'
|
||||
}
|
||||
});
|
||||
|
||||
public setRequestData (optionsArg: {
|
||||
requestAgent: string;
|
||||
requestUrl: string;
|
||||
requestMethod: string;
|
||||
}) {
|
||||
this.data = {
|
||||
...this.data,
|
||||
...{
|
||||
requestAgent: optionsArg.requestAgent,
|
||||
requestUrl: optionsArg.requestUrl,
|
||||
requestMethod: optionsArg.requestMethod,
|
||||
requestStartTime: Date.now()
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public setResponseData(optionsArg: {
|
||||
responseStatus: number,
|
||||
responseEndTime: number,
|
||||
}) {
|
||||
this.data = {
|
||||
...this.data,
|
||||
...{
|
||||
responseStatus: optionsArg.responseStatus,
|
||||
responseEndTime: optionsArg.responseEndTime
|
||||
}
|
||||
};
|
||||
this.sendLogs();
|
||||
}
|
||||
|
||||
public async sendLogs() {
|
||||
await this.smartlog.log('info', `
|
||||
Got a ${this.data.requestMethod} request from ${this.data.requestAgent} to
|
||||
${this.data.requestUrl}
|
||||
that took ${this.data.responseEndTime - this.data.requestStartTime}ms to resolve with status ${this.data.responseStatus}.`, this.data);
|
||||
this.finishedDeferred.resolve();
|
||||
}
|
||||
}
|
26
ts_edgeworker/analytics/smartlog.ts
Normal file
26
ts_edgeworker/analytics/smartlog.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import * as smartlogInterfaces from '@push.rocks/smartlog-interfaces';
|
||||
import type { EdgeWorker } from '../classes.edgeworker.js';
|
||||
|
||||
export class SmartlogDestination implements smartlogInterfaces.ILogDestination {
|
||||
public edgeWorkerRef: EdgeWorker;
|
||||
|
||||
constructor(edgeworkerRefArg: EdgeWorker) {
|
||||
this.edgeWorkerRef = edgeworkerRefArg;
|
||||
}
|
||||
|
||||
public async handleLog(logPackageArg: smartlogInterfaces.ILogPackage) {
|
||||
if (this.edgeWorkerRef.options.smartlogConfig) {
|
||||
const requestBody: smartlogInterfaces.ILogPackageAuthenticated = {
|
||||
auth: this.edgeWorkerRef.options.smartlogConfig.token,
|
||||
logPackage: logPackageArg,
|
||||
};
|
||||
await fetch(this.edgeWorkerRef.options.smartlogConfig.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
67
ts_edgeworker/classes.domainrouter.ts
Normal file
67
ts_edgeworker/classes.domainrouter.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
import * as plugins from './plugins.js';
|
||||
import { WorkerEvent } from './classes.workerevent.js';
|
||||
|
||||
import * as domainInstructions from './domaininstructions/index.js';
|
||||
|
||||
export class DomainRouter {
|
||||
private smartmatches: plugins.smartmatch.SmartMatch[] = [];
|
||||
|
||||
constructor() {
|
||||
for (const key of Object.keys(domainInstructions.instructionObject)) {
|
||||
this.smartmatches.push(new plugins.smartmatch.SmartMatch(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cworkerevent
|
||||
*/
|
||||
public routeToResponder(cworkerevent: WorkerEvent) {
|
||||
const match = this.smartmatches.find(smartmatchArg => {
|
||||
return smartmatchArg.match(cworkerevent.request.url);
|
||||
});
|
||||
cworkerevent.responderInstruction = match
|
||||
? domainInstructions.instructionObject[match.wildcard]
|
||||
: {
|
||||
type: 'cache'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* rendertronRouter
|
||||
*/
|
||||
public checkWetherReRouteToRendertron(cworkerevent: WorkerEvent) {
|
||||
let needsRendertron = false;
|
||||
for (const botAgentIdentifier of domainInstructions.botUserAgents) {
|
||||
if (needsRendertron) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
cworkerevent.request.headers.get('user-agent') &&
|
||||
cworkerevent.request.headers.get('user-agent').toLowerCase().includes(botAgentIdentifier.toLowerCase()) &&
|
||||
!cworkerevent.request.url.includes('lossless.one')
|
||||
) {
|
||||
needsRendertron = true;
|
||||
}
|
||||
}
|
||||
if (needsRendertron) {
|
||||
cworkerevent.routedThroughRendertron = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check wether this is a preflight request that should be handled
|
||||
*/
|
||||
public checkWetherIsPreflight (cworkerevent: WorkerEvent) {
|
||||
if (
|
||||
cworkerevent.request.method === 'OPTIONS' &&
|
||||
cworkerevent.request.headers.get('Origin') !== null &&
|
||||
cworkerevent.request.headers.get('Access-Control-Request-Method') !== null &&
|
||||
cworkerevent.request.headers.get('Access-Control-Request-Headers') !== null
|
||||
) {
|
||||
cworkerevent.isPreflight = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
73
ts_edgeworker/classes.edgeworker.ts
Normal file
73
ts_edgeworker/classes.edgeworker.ts
Normal file
@ -0,0 +1,73 @@
|
||||
// imports
|
||||
import { WorkerEvent } from './classes.workerevent.js';
|
||||
import { DomainRouter } from './classes.domainrouter.js';
|
||||
import * as plugins from './plugins.js';
|
||||
import * as responders from './responders/index.js';
|
||||
|
||||
export interface IEdgeWorkerOptions {
|
||||
smartlogConfig?: {
|
||||
endpoint: string;
|
||||
token: string;
|
||||
}
|
||||
}
|
||||
|
||||
export class EdgeWorker {
|
||||
public options: IEdgeWorkerOptions;
|
||||
domainRouter: DomainRouter;
|
||||
|
||||
constructor(optionsArg: IEdgeWorkerOptions = {}) {
|
||||
this.options = optionsArg;
|
||||
this.domainRouter = new DomainRouter();
|
||||
addEventListener('fetch', this.fetchFunction as any);
|
||||
}
|
||||
|
||||
public async fetchFunction (eventArg: plugins.cloudflareTypes.FetchEvent) {
|
||||
if (new URL(eventArg.request.url).pathname.startsWith('/socket.io')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cworkerEvent = new WorkerEvent({
|
||||
edgeWorkerRef: this,
|
||||
event: eventArg,
|
||||
passThroughOnException: true
|
||||
});
|
||||
|
||||
// lets answer basic reuest things
|
||||
responders.timeoutResponder(cworkerEvent);
|
||||
cworkerEvent.hasResponse ? null : await responders.urlFormattingResponder(cworkerEvent);
|
||||
|
||||
// lets route the domain
|
||||
this.domainRouter.routeToResponder(cworkerEvent);
|
||||
this.domainRouter.checkWetherReRouteToRendertron(cworkerEvent);
|
||||
this.domainRouter.checkWetherIsPreflight(cworkerEvent);
|
||||
|
||||
// guardresponder
|
||||
cworkerEvent.hasResponse ? null : await responders.guardResponder(cworkerEvent);
|
||||
|
||||
// lets process all requests that need rendertron
|
||||
cworkerEvent.hasResponse ? null : await responders.rendertronResponder(cworkerEvent);
|
||||
|
||||
// lets process all requests that are preflight requests
|
||||
cworkerEvent.hasResponse ? null : await responders.preflightResponder(cworkerEvent);
|
||||
|
||||
switch (cworkerEvent.responderInstruction.type) {
|
||||
case 'cache':
|
||||
cworkerEvent.hasResponse ? null : await responders.cacheResponder(cworkerEvent);
|
||||
break;
|
||||
case 'origin':
|
||||
cworkerEvent.hasResponse ? null : await responders.originResponder(cworkerEvent);
|
||||
break;
|
||||
case 'redirect':
|
||||
cworkerEvent.hasResponse ? null : await responders.adsTxtResponder(cworkerEvent);
|
||||
break;
|
||||
case 'static':
|
||||
cworkerEvent.hasResponse ? null : await responders.staticResponder(cworkerEvent);
|
||||
break;
|
||||
case 'ads.txt':
|
||||
cworkerEvent.hasResponse ? null : await responders.adsTxtResponder(cworkerEvent);
|
||||
break;
|
||||
}
|
||||
// cworkerEvent.hasResponse ? null : await responders.kvResponder(cworkerEvent);
|
||||
cworkerEvent.hasResponse ? null : await responders.errorResponder(cworkerEvent);
|
||||
};
|
||||
}
|
37
ts_edgeworker/classes.kvhandler.ts
Normal file
37
ts_edgeworker/classes.kvhandler.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
import * as plugins from './plugins.js';
|
||||
|
||||
declare var lokv: plugins.cloudflareTypes.KVNamespace;
|
||||
|
||||
/**
|
||||
* an abstraction for the workerd KV store
|
||||
*/
|
||||
export class KVHandler {
|
||||
private getSafeIdentifier(urlString: string) {
|
||||
return encodeURI(urlString);
|
||||
}
|
||||
|
||||
async getFromKv(keyIdentifier: string) {
|
||||
const key = this.getSafeIdentifier(keyIdentifier);
|
||||
const valueString = await lokv.get(key);
|
||||
return valueString;
|
||||
}
|
||||
|
||||
async putInKv(keyIdentifier: string, valueForStorage: string) {
|
||||
const key = this.getSafeIdentifier(keyIdentifier);
|
||||
const value = valueForStorage;
|
||||
await lokv.put(key, value);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes a key/value from the cache
|
||||
* @param keyIdentifier
|
||||
*/
|
||||
async deleteInKv(keyIdentifier: string) {
|
||||
const cacheKey = this.getSafeIdentifier(keyIdentifier);
|
||||
await lokv.delete(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
export const kvHandlerInstance = new KVHandler();
|
46
ts_edgeworker/classes.responsekv.ts
Normal file
46
ts_edgeworker/classes.responsekv.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import { kvHandlerInstance } from './classes.kvhandler.js';
|
||||
|
||||
declare var lokv: plugins.cloudflareTypes.KVNamespace;
|
||||
|
||||
interface IKVResponseObject {
|
||||
headers: { [key: string]: string };
|
||||
version: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export class ResponseKv {
|
||||
public async storeResponse(urlIdentifier: string, responseArg: any) {
|
||||
const headers: { [key: string]: string } = {};
|
||||
for (const kv of responseArg.headers.entries()) {
|
||||
headers[kv[0]] = kv[1];
|
||||
}
|
||||
const kvResponseForStorage: IKVResponseObject = {
|
||||
headers,
|
||||
version: '1.0.0',
|
||||
body: await responseArg.text()
|
||||
};
|
||||
await kvHandlerInstance.putInKv(urlIdentifier, JSON.stringify(kvResponseForStorage));
|
||||
}
|
||||
|
||||
public async getResponse(urlIdentifier: string): Promise<Response> {
|
||||
const kvValue = await kvHandlerInstance.getFromKv(urlIdentifier);
|
||||
if (kvValue) {
|
||||
let kvResponse: IKVResponseObject;
|
||||
try {
|
||||
kvResponse = JSON.parse(kvValue);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return null;
|
||||
}
|
||||
const headers = new Headers();
|
||||
for (const key of Object.keys(kvResponse.headers)) {
|
||||
headers.append(key, kvResponse.headers[key]);
|
||||
}
|
||||
headers.append('SERVEZONE_ROUTE', 'CLOUDFLARE_EDGE_LOKV');
|
||||
return new Response(kvResponse.body, { headers: headers });
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
102
ts_edgeworker/classes.workerevent.ts
Normal file
102
ts_edgeworker/classes.workerevent.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
import * as plugins from './plugins.js';
|
||||
import * as helpers from './helpers/index.js';
|
||||
import { DomainRouter } from './classes.domainrouter.js';
|
||||
import { Analyzer } from './analytics/analyzer.js';
|
||||
import type { EdgeWorker } from './classes.edgeworker.js';
|
||||
|
||||
export interface ICworkerEventOptions {
|
||||
event: plugins.cloudflareTypes.FetchEvent
|
||||
edgeWorkerRef: EdgeWorker;
|
||||
passThroughOnException?: boolean;
|
||||
}
|
||||
|
||||
|
||||
export class WorkerEvent {
|
||||
public options: ICworkerEventOptions;
|
||||
|
||||
public analyzer: Analyzer;
|
||||
private responseDeferred: plugins.smartpromise.Deferred<any>;
|
||||
private waitUntilDeferred: plugins.smartpromise.Deferred<any>;
|
||||
|
||||
private response: Response = null;
|
||||
private waitList = [];
|
||||
|
||||
// routing settings
|
||||
public responderInstruction: interfaces.IResponderInstruction;
|
||||
public routedThroughRendertron: boolean = false;
|
||||
public isPreflight: boolean = false;
|
||||
|
||||
public request: plugins.cloudflareTypes.Request;
|
||||
|
||||
public parsedUrl: URL;
|
||||
|
||||
constructor(optionsArg: ICworkerEventOptions) {
|
||||
this.options = optionsArg;
|
||||
|
||||
// lets create an Analyzer for this request
|
||||
this.analyzer = new Analyzer(this);
|
||||
|
||||
// lets make sure we always answer
|
||||
this.options.passThroughOnException ? this.options.event.passThroughOnException() : null;
|
||||
|
||||
// lets set up some better asnyc behaviour
|
||||
this.waitUntilDeferred = plugins.smartpromise.defer();
|
||||
this.responseDeferred = plugins.smartpromise.defer();
|
||||
this.addToWaitList(this.analyzer.finishedDeferred.promise);
|
||||
|
||||
// lets entangle the event with this class instance
|
||||
this.request = this.options.event.request;
|
||||
|
||||
// lets start with analytics
|
||||
this.analyzer.setRequestData({
|
||||
requestAgent: this.request.headers.get('user-agent'),
|
||||
requestMethod: this.request.method,
|
||||
requestUrl: this.request.url
|
||||
});
|
||||
|
||||
|
||||
this.options.event.respondWith(this.responseDeferred.promise);
|
||||
this.options.event.waitUntil(this.waitUntilDeferred.promise);
|
||||
|
||||
// lets parse the url
|
||||
this.parsedUrl = new URL(this.request.url);
|
||||
|
||||
// lets check the waitlist
|
||||
this.checkWaitList();
|
||||
console.log(`Got request for ${this.request.url}`);
|
||||
}
|
||||
|
||||
get hasResponse () {
|
||||
let returnValue: boolean;
|
||||
this.response ? returnValue = true : returnValue = false;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public addToWaitList(promiseArg: Promise<any>) {
|
||||
this.waitList.push(promiseArg);
|
||||
}
|
||||
|
||||
private async checkWaitList() {
|
||||
await this.responseDeferred.promise;
|
||||
const currentWaitList = this.waitList;
|
||||
this.waitList = [];
|
||||
await Promise.all(currentWaitList);
|
||||
if (this.waitList.length > 0) {
|
||||
this.checkWaitList();
|
||||
} else {
|
||||
this.waitUntilDeferred.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
public setResponse (responseArg: Response) {
|
||||
this.response = responseArg;
|
||||
this.responseDeferred.resolve(responseArg);
|
||||
this.analyzer.setResponseData({
|
||||
responseStatus: this.response.status,
|
||||
responseEndTime: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
36
ts_edgeworker/domaininstructions/botuseragents.ts
Normal file
36
ts_edgeworker/domaininstructions/botuseragents.ts
Normal file
@ -0,0 +1,36 @@
|
||||
export const botUserAgents = [
|
||||
// Baidu
|
||||
'baiduspider',
|
||||
'embedly',
|
||||
|
||||
// Facebook
|
||||
'facebookexternalhit',
|
||||
|
||||
// Ghost
|
||||
'Ghost',
|
||||
|
||||
// Microsoft
|
||||
'bingbot',
|
||||
'BingPreview',
|
||||
'linkedinbot',
|
||||
'MissinglettrBot',
|
||||
'msnbot',
|
||||
'outbrain',
|
||||
'pinterest',
|
||||
'quora link preview',
|
||||
'rogerbot',
|
||||
'showyoubot',
|
||||
'slackbot',
|
||||
'TelegramBot',
|
||||
|
||||
// Twitter
|
||||
'twitterbot',
|
||||
'vkShare',
|
||||
'W3C_Validator',
|
||||
|
||||
// WhatsApp
|
||||
'whatsapp',
|
||||
|
||||
// woorank
|
||||
'woorank'
|
||||
];
|
7
ts_edgeworker/domaininstructions/domaininstructions.ts
Normal file
7
ts_edgeworker/domaininstructions/domaininstructions.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
|
||||
export const instructionObject: { [key: string]: interfaces.IResponderInstruction } = {
|
||||
'*/ads.txt': {
|
||||
type: 'ads.txt',
|
||||
}
|
||||
};
|
2
ts_edgeworker/domaininstructions/index.ts
Normal file
2
ts_edgeworker/domaininstructions/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './botuseragents.js';
|
||||
export * from './domaininstructions.js';
|
9
ts_edgeworker/helpers/checks.ts
Normal file
9
ts_edgeworker/helpers/checks.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
declare var lokv: plugins.cloudflareTypes.KVNamespace;
|
||||
export const checkLokv = () => {
|
||||
if (!lokv) {
|
||||
throw new Error('lokv not defined!');
|
||||
} else {
|
||||
console.log('lokv present!');
|
||||
}
|
||||
};
|
1
ts_edgeworker/helpers/index.ts
Normal file
1
ts_edgeworker/helpers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './checks.js';
|
1
ts_edgeworker/index.ts
Normal file
1
ts_edgeworker/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './classes.edgeworker.js';
|
9
ts_edgeworker/interfaces/custom.ts
Normal file
9
ts_edgeworker/interfaces/custom.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { WorkerEvent } from "../classes.workerevent.js";
|
||||
|
||||
export interface IResponderInstruction {
|
||||
type: 'origin' | 'cache' | 'static' | 'redirect' | 'ads.txt';
|
||||
cacheClientSideForMin?: number;
|
||||
redirectUrl?: string;
|
||||
}
|
||||
|
||||
export type TRequestResponser = (workerEventArg: WorkerEvent) => Promise<void>;
|
1
ts_edgeworker/interfaces/index.ts
Normal file
1
ts_edgeworker/interfaces/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './custom.js';
|
21
ts_edgeworker/plugins.ts
Normal file
21
ts_edgeworker/plugins.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// @pushrocks scope
|
||||
import * as smartdelay from '@push.rocks/smartdelay';
|
||||
import * as smartlog from '@push.rocks/smartlog';
|
||||
import * as smartlogInterfaces from '@push.rocks/smartlog-interfaces';
|
||||
import * as smartmatch from '@push.rocks/smartmatch';
|
||||
import * as smartpromise from '@push.rocks/smartpromise';
|
||||
|
||||
export {
|
||||
smartdelay,
|
||||
smartlog,
|
||||
smartlogInterfaces,
|
||||
smartmatch,
|
||||
smartpromise
|
||||
};
|
||||
|
||||
// cloudflarea
|
||||
import * as cloudflareTypes from '@cloudflare/workers-types';
|
||||
|
||||
export {
|
||||
cloudflareTypes
|
||||
}
|
14
ts_edgeworker/responders/adstxt.responder.ts
Normal file
14
ts_edgeworker/responders/adstxt.responder.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
|
||||
export const adsTxtResponder: interfaces.TRequestResponser = async (cWorkerEventArg: WorkerEvent) => {
|
||||
if (cWorkerEventArg.responderInstruction.type === 'ads.txt') {
|
||||
const response = new Response('google.com, pub-4104137977476459, DIRECT, f08c47fec0942fa0\n', {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain; charset=utf-8'
|
||||
}
|
||||
})
|
||||
cWorkerEventArg.setResponse(response);
|
||||
}
|
||||
|
||||
};
|
92
ts_edgeworker/responders/cache.responder.ts
Normal file
92
ts_edgeworker/responders/cache.responder.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
import { kvHandlerInstance } from '../classes.kvhandler.js';
|
||||
|
||||
declare const fetch: plugins.cloudflareTypes.Fetcher['fetch'];
|
||||
|
||||
declare var caches: any;
|
||||
export const cacheResponder: interfaces.TRequestResponser = async (cworkerEventArg: WorkerEvent) => {
|
||||
const host = cworkerEventArg.request.headers.get('Host');
|
||||
const appHashKey = `${host.toLowerCase()}_appHash`;
|
||||
const appHash = await kvHandlerInstance.getFromKv(appHashKey);
|
||||
|
||||
const cache = caches.default;
|
||||
let response: Response = await cache.match(cworkerEventArg.request);
|
||||
|
||||
if (
|
||||
response &&
|
||||
response.headers.get('appHash') &&
|
||||
response.headers.get('appHash') !== appHash
|
||||
) {
|
||||
response = null;
|
||||
}
|
||||
|
||||
if (response) {
|
||||
cworkerEventArg.setResponse(response);
|
||||
} else {
|
||||
response = await handleNewRequest(cworkerEventArg.request);
|
||||
if (response) {
|
||||
cworkerEventArg.addToWaitList(new Promise<void>(async (resolve, reject) => {
|
||||
const newAppHash = response.headers.get('appHash');
|
||||
if (newAppHash) {
|
||||
await kvHandlerInstance.putInKv(appHashKey, newAppHash);
|
||||
}
|
||||
resolve();
|
||||
}));
|
||||
cworkerEventArg.addToWaitList(buildCacheResponse(cache, cworkerEventArg.request, response));
|
||||
cworkerEventArg.setResponse(response);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Request} originalRequest
|
||||
*/
|
||||
const handleNewRequest = async (originalRequest: plugins.cloudflareTypes.Request): Promise<Response> => {
|
||||
console.log('answering from origin');
|
||||
const originResponse: any = await fetch(
|
||||
originalRequest
|
||||
);
|
||||
|
||||
// lets capture status
|
||||
if (originResponse.status > 399) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const responseClientPassThroughStream = new TransformStream();
|
||||
originResponse.body.pipeTo(responseClientPassThroughStream.writable);
|
||||
|
||||
// build response for client
|
||||
const clientHeaders = new Headers();
|
||||
for (const kv of originResponse.headers.entries()) {
|
||||
clientHeaders.append(kv[0], kv[1]);
|
||||
}
|
||||
clientHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_ORIGIN_INITIAL');
|
||||
const responseForClient = new Response(responseClientPassThroughStream.readable, {
|
||||
...originResponse,
|
||||
headers: clientHeaders
|
||||
});
|
||||
|
||||
// lets return the responses
|
||||
return responseForClient;
|
||||
};
|
||||
|
||||
const buildCacheResponse = async (cache, matchRequest: plugins.cloudflareTypes.Request, originResponse: any) => {
|
||||
const cacheHeaders = new Headers();
|
||||
for (const kv of originResponse.headers.entries()) {
|
||||
cacheHeaders.append(kv[0], kv[1]);
|
||||
}
|
||||
cacheHeaders.delete('SERVEZONE_ROUTE');
|
||||
cacheHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_CACHE');
|
||||
cacheHeaders.delete('Cache-Control');
|
||||
cacheHeaders.append('Cache-Control', 'public, max-age=60');
|
||||
cacheHeaders.delete('Expires');
|
||||
cacheHeaders.append('Expires', new Date(Date.now() + 60 * 1000).toUTCString());
|
||||
|
||||
const responseForCache = new Response(await originResponse.clone().body, {
|
||||
...originResponse,
|
||||
headers: cacheHeaders
|
||||
});
|
||||
await cache.put(matchRequest, responseForCache);
|
||||
};
|
6
ts_edgeworker/responders/error.responder.ts
Normal file
6
ts_edgeworker/responders/error.responder.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
export const errorResponder: interfaces.TRequestResponser = async (cWorkerEvent: WorkerEvent) => {
|
||||
const errorResponse = await fetch('https://nullresolve.lossless.one/status/firewall');
|
||||
cWorkerEvent.setResponse(errorResponse);
|
||||
};
|
8
ts_edgeworker/responders/guard.responder.ts
Normal file
8
ts_edgeworker/responders/guard.responder.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
export const guardResponder: interfaces.TRequestResponser = async (cWorkerEvent: WorkerEvent) => {
|
||||
if (cWorkerEvent.parsedUrl.pathname.endsWith('.map')) {
|
||||
const errorResponse = await fetch('https://nullresolve.lossless.one/status/firewall');
|
||||
cWorkerEvent.setResponse(errorResponse);
|
||||
}
|
||||
};
|
12
ts_edgeworker/responders/index.ts
Normal file
12
ts_edgeworker/responders/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export * from './adstxt.responder.js';
|
||||
export * from './cache.responder.js';
|
||||
export * from './urlformatting.responder.js';
|
||||
export * from './error.responder.js';
|
||||
export * from './guard.responder.js';
|
||||
export * from './kv.responder.js';
|
||||
export * from './origin.responder.js';
|
||||
export * from './preflight.responder.js';
|
||||
export * from './redirect.reponder.js';
|
||||
export * from './rendertron.responder.js';
|
||||
export * from './static.responder.js';
|
||||
export * from './timeout.responder.js';
|
34
ts_edgeworker/responders/kv.responder.ts
Normal file
34
ts_edgeworker/responders/kv.responder.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
import { ResponseKv } from '../classes.responsekv.js';
|
||||
|
||||
declare const fetch: plugins.cloudflareTypes.Fetcher['fetch'];
|
||||
|
||||
export const kvResponder: interfaces.TRequestResponser = async (cworkerEventArg: WorkerEvent) => {
|
||||
const responseKvInstance = new ResponseKv();
|
||||
let response = await responseKvInstance.getResponse(cworkerEventArg.request.url);
|
||||
if (response) {
|
||||
console.log('Got response from KV');
|
||||
} else {
|
||||
response = await handleNewRequest(cworkerEventArg.request, responseKvInstance);
|
||||
}
|
||||
cworkerEventArg.setResponse(response);
|
||||
};
|
||||
|
||||
const handleNewRequest = async (request: plugins.cloudflareTypes.Request, responseKvInstance: ResponseKv) => {
|
||||
const originResponse: any = await fetch(request);
|
||||
// build response for cache
|
||||
const cacheHeaders = new Headers();
|
||||
for (const kv of originResponse.headers.entries()) {
|
||||
cacheHeaders.append(kv[0], kv[1]);
|
||||
}
|
||||
cacheHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_KVRESPONSE');
|
||||
cacheHeaders.append('Cache-Control', 'max-age=600');
|
||||
const responseForKV = new Response(await originResponse.body, {
|
||||
...originResponse,
|
||||
headers: cacheHeaders
|
||||
});
|
||||
await responseKvInstance.storeResponse(request.url, responseForKV.clone());
|
||||
return responseForKV.clone();
|
||||
};
|
31
ts_edgeworker/responders/origin.responder.ts
Normal file
31
ts_edgeworker/responders/origin.responder.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
|
||||
declare const fetch: plugins.cloudflareTypes.Fetcher['fetch'];
|
||||
|
||||
export const originResponder: interfaces.TRequestResponser = async (eventArg: WorkerEvent) => {
|
||||
const originResponse: any = await fetch(eventArg.request);
|
||||
// lets capture status
|
||||
if (originResponse.status > 399) {
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = new Headers();
|
||||
for (const kv of originResponse.headers.entries()) {
|
||||
headers.append(kv[0], kv[1]);
|
||||
}
|
||||
headers.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_FASTORIGIN');
|
||||
|
||||
const responsePassThroughStream = new TransformStream();
|
||||
originResponse.body.pipeTo(responsePassThroughStream.writable);
|
||||
|
||||
// response
|
||||
|
||||
const responseForClient = new Response(responsePassThroughStream.readable, {
|
||||
...originResponse,
|
||||
headers,
|
||||
});
|
||||
|
||||
eventArg.setResponse(responseForClient);
|
||||
};
|
18
ts_edgeworker/responders/preflight.responder.ts
Normal file
18
ts_edgeworker/responders/preflight.responder.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
|
||||
export const preflightResponder: interfaces.TRequestResponser = async (eventArg: WorkerEvent) => {
|
||||
if (eventArg.isPreflight) {
|
||||
const corsHeaders = new Headers();
|
||||
corsHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_PREFLIGHT');
|
||||
corsHeaders.append('Access-Control-Allow-Origin', '*');
|
||||
corsHeaders.append('Access-Control-Allow-Methods', '*');
|
||||
corsHeaders.append('Access-Control-Allow-Headers', '*');
|
||||
|
||||
eventArg.setResponse(
|
||||
new Response(null, {
|
||||
headers: corsHeaders,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
9
ts_edgeworker/responders/redirect.reponder.ts
Normal file
9
ts_edgeworker/responders/redirect.reponder.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
|
||||
export const redirectResponder: interfaces.TRequestResponser = async (cWorkerEventArg: WorkerEvent) => {
|
||||
if (cWorkerEventArg.responderInstruction.type === 'redirect') {
|
||||
cWorkerEventArg.setResponse(Response.redirect(cWorkerEventArg.responderInstruction.redirectUrl, 302));
|
||||
}
|
||||
|
||||
};
|
22
ts_edgeworker/responders/rendertron.responder.ts
Normal file
22
ts_edgeworker/responders/rendertron.responder.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
|
||||
export const rendertronResponder = async (cworkerevent: WorkerEvent) => {
|
||||
if (cworkerevent.routedThroughRendertron) {
|
||||
const oldHeaders: any = cworkerevent.request.headers;
|
||||
const rendertronHeaders = new Headers();
|
||||
for (const kv of oldHeaders.entries()) {
|
||||
const headerName = kv[0];
|
||||
const headerValue = headerName === 'user-agent' ? 'Lossless Rendertron' : kv[1];
|
||||
rendertronHeaders.append(headerName, headerValue);
|
||||
}
|
||||
const rendertronRequest = new Request(
|
||||
`https://rendertron.lossless.one/render/${cworkerevent.request.url}`,
|
||||
{
|
||||
method: cworkerevent.request.method,
|
||||
headers: rendertronHeaders
|
||||
}
|
||||
);
|
||||
cworkerevent.setResponse(await fetch(rendertronRequest));
|
||||
}
|
||||
};
|
31
ts_edgeworker/responders/static.responder.ts
Normal file
31
ts_edgeworker/responders/static.responder.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
|
||||
export const staticResponder: interfaces.TRequestResponser = async (cWorkerEventArg: WorkerEvent) => {
|
||||
if (cWorkerEventArg.responderInstruction.type === 'static') {
|
||||
const originResponse: any = await fetch(
|
||||
`https://statichost.lossless.one/resolve?url=${encodeURI(cWorkerEventArg.request.url)}`
|
||||
);
|
||||
|
||||
const cacheHeaders = new Headers();
|
||||
for (const kv of originResponse.headers.entries()) {
|
||||
cacheHeaders.append(kv[0], kv[1]);
|
||||
}
|
||||
cacheHeaders.delete('SERVEZONE_ROUTE');
|
||||
cacheHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_STATICHOST');
|
||||
|
||||
if (cWorkerEventArg.responderInstruction.cacheClientSideForMin) {
|
||||
cacheHeaders.delete('Cache-Control');
|
||||
cacheHeaders.append('Cache-Control', `public, max-age=${cWorkerEventArg.responderInstruction.cacheClientSideForMin * 60}`);
|
||||
cacheHeaders.delete('Expires');
|
||||
cacheHeaders.append('Expires', new Date(Date.now() + cWorkerEventArg.responderInstruction.cacheClientSideForMin * 1000).toUTCString());
|
||||
}
|
||||
|
||||
const responseForClient = new Response(await originResponse.clone().body, {
|
||||
...originResponse,
|
||||
headers: cacheHeaders
|
||||
});
|
||||
|
||||
cWorkerEventArg.setResponse(responseForClient);
|
||||
}
|
||||
};
|
23
ts_edgeworker/responders/timeout.responder.ts
Normal file
23
ts_edgeworker/responders/timeout.responder.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
|
||||
export const timeoutResponder: interfaces.TRequestResponser = async (cWorkerEvent: WorkerEvent) => {
|
||||
await plugins.smartdelay.delayFor(10000);
|
||||
if (cWorkerEvent.routedThroughRendertron) {
|
||||
await plugins.smartdelay.delayFor(10000);
|
||||
}
|
||||
if (!cWorkerEvent.hasResponse) {
|
||||
const errorResponse = await fetch(
|
||||
`https://nullresolve.lossless.one/custom?title=${encodeURI(
|
||||
`Lossless Network: Request Cancellation!`
|
||||
)}&heading=${encodeURI(`Error: Request Cancellation`)}&text=${encodeURI(
|
||||
`Lossless Network could not decide how to respond to this request within 5 seconds. Therefore it timed out and has been canceled.
|
||||
<p>requestUrl: ${cWorkerEvent.request.url}<br>
|
||||
requestTime: ${Date.now()}<br>
|
||||
referenceNumber: xxxxxx</p>`
|
||||
)}`
|
||||
);
|
||||
cWorkerEvent.setResponse(errorResponse);
|
||||
}
|
||||
};
|
21
ts_edgeworker/responders/urlformatting.responder.ts
Normal file
21
ts_edgeworker/responders/urlformatting.responder.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import * as interfaces from '../interfaces/index.js';
|
||||
import { WorkerEvent } from '../classes.workerevent.js';
|
||||
|
||||
export const urlFormattingResponder: interfaces.TRequestResponser = async (eventArg: WorkerEvent) => {
|
||||
let shouldCorrect = false;
|
||||
const correctedUrl = new URL(eventArg.request.url);
|
||||
if (eventArg.parsedUrl.hostname.startsWith('www.')) {
|
||||
shouldCorrect = true;
|
||||
correctedUrl.hostname = eventArg.parsedUrl.hostname.substring(
|
||||
4,
|
||||
eventArg.parsedUrl.hostname.length
|
||||
);
|
||||
}
|
||||
if (eventArg.parsedUrl.protocol.startsWith('http:')) {
|
||||
shouldCorrect = true;
|
||||
correctedUrl.protocol = 'https:';
|
||||
}
|
||||
if (shouldCorrect) {
|
||||
eventArg.setResponse(Response.redirect(`${correctedUrl.protocol}//${correctedUrl.host}${correctedUrl.pathname}${correctedUrl.search}`, 301));
|
||||
}
|
||||
};
|
7
ts_edgeworker/versionhandler.ts
Normal file
7
ts_edgeworker/versionhandler.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
|
||||
export class VersionHandler {
|
||||
|
||||
}
|
||||
|
||||
export const versionHandlerInstance = new VersionHandler();
|
@ -1,5 +1,5 @@
|
||||
import * as plugins from './typedserver_web.plugins.js';
|
||||
import * as interfaces from '../ts_interfaces/index.js';
|
||||
import * as interfaces from '../dist_ts_interfaces/index.js';
|
||||
import { logger } from './typedserver_web.logger.js';
|
||||
logger.log('info', `TypedServer-Devtools initialized!`);
|
||||
|
||||
|
@ -2,7 +2,10 @@
|
||||
import * as env from './env.js';
|
||||
declare var self: env.ServiceWindow;
|
||||
|
||||
import { LosslessServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
import { ServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
|
||||
const losslessServiceWorkerInstance = new LosslessServiceWorker(self);
|
||||
export const getServiceWorkerInstance = async () => {
|
||||
const sw = new ServiceWorker(self);
|
||||
return sw;
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as interfaces from './env.js';
|
||||
import { logger } from './serviceworker.logging.js';
|
||||
import { LosslessServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
import { ServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
|
||||
export class CacheManager {
|
||||
public losslessServiceWorkerRef: LosslessServiceWorker;
|
||||
public losslessServiceWorkerRef: ServiceWorker;
|
||||
|
||||
public usedCacheNames = {
|
||||
runtimeCacheName: 'runtime'
|
||||
};
|
||||
|
||||
constructor(losslessServiceWorkerRefArg: LosslessServiceWorker) {
|
||||
constructor(losslessServiceWorkerRefArg: ServiceWorker) {
|
||||
this.losslessServiceWorkerRef = losslessServiceWorkerRefArg;
|
||||
this._setupCache();
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import { LosslessServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
import { ServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
|
||||
export class NetworkManager {
|
||||
public serviceWorkerRef: LosslessServiceWorker;
|
||||
public serviceWorkerRef: ServiceWorker;
|
||||
public webRequest: plugins.webrequest.WebRequest;
|
||||
|
||||
public previousState: string;
|
||||
|
||||
constructor(serviceWorkerRefArg: LosslessServiceWorker) {
|
||||
constructor(serviceWorkerRefArg: ServiceWorker) {
|
||||
this.serviceWorkerRef = serviceWorkerRefArg;
|
||||
this.webRequest = new plugins.webrequest.WebRequest();
|
||||
this.getConnection()?.addEventListener('change', () => {
|
||||
@ -24,7 +24,7 @@ export class NetworkManager {
|
||||
}
|
||||
|
||||
public getEffectiveType() {
|
||||
return this.getConnection()?.effectiveType || '4g';
|
||||
return this.getConnection()?.effectiveType || '4g';
|
||||
}
|
||||
|
||||
public updateConnectionStatus() {
|
||||
|
@ -11,13 +11,14 @@ import { logger } from './serviceworker.logging.js';
|
||||
import { UpdateManager } from './serviceworker.classes.updatemanager.js';
|
||||
import { NetworkManager } from './serviceworker.classes.networkmanager.js';
|
||||
import { TaskManager } from './serviceworker.classes.taskmanager.js';
|
||||
import { ServiceworkerBackend } from './classes.backend.js';
|
||||
|
||||
export class LosslessServiceWorker {
|
||||
export class ServiceWorker {
|
||||
// STATIC
|
||||
|
||||
// INSTANCE
|
||||
public serviceWindowRef: interfaces.ServiceWindow;
|
||||
public leleServiceWorkerBackend: plugins.leleServiceworker.LosslessServiceworkerBackend;
|
||||
public leleServiceWorkerBackend: ServiceworkerBackend;
|
||||
|
||||
public cacheManager: CacheManager;
|
||||
|
||||
@ -29,7 +30,7 @@ export class LosslessServiceWorker {
|
||||
constructor(selfArg: interfaces.ServiceWindow) {
|
||||
logger.log('info', `Service worker instantiating at ${Date.now()}`);
|
||||
this.serviceWindowRef = selfArg;
|
||||
this.leleServiceWorkerBackend = plugins.leleServiceworker.getServiceWorkerBackend({
|
||||
this.leleServiceWorkerBackend = new ServiceworkerBackend({
|
||||
self: selfArg,
|
||||
purgeCache: async (reqArg) => {
|
||||
await this.cacheManager.cleanCaches(),
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import { LosslessServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
import { ServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
|
||||
/**
|
||||
* Taskmanager
|
||||
* should use times allocated by browser
|
||||
*/
|
||||
export class TaskManager {
|
||||
public serviceworkerRef: LosslessServiceWorker;
|
||||
public serviceworkerRef: ServiceWorker;
|
||||
|
||||
constructor(serviceWorkerRefArg: LosslessServiceWorker) {
|
||||
constructor(serviceWorkerRefArg: ServiceWorker) {
|
||||
this.serviceworkerRef = serviceWorkerRefArg;
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import { LosslessServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
import * as interfaces from '../dist_ts_interfaces/index.js';
|
||||
import { ServiceWorker } from './serviceworker.classes.serviceworker.js';
|
||||
import { logger } from './serviceworker.logging.js';
|
||||
import { CacheManager } from './serviceworker.classes.cachemanager.js';
|
||||
|
||||
export class UpdateManager {
|
||||
public lastUpdateCheck: number = 0;
|
||||
public lastVersionInfo: plugins.lointServiceworker.IRequest_Serviceworker_Backend_VersionInfo['response'];
|
||||
public lastVersionInfo: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'];
|
||||
|
||||
public serviceworkerRef: LosslessServiceWorker;
|
||||
public serviceworkerRef: ServiceWorker;
|
||||
|
||||
constructor(serviceWorkerRefArg: LosslessServiceWorker) {
|
||||
constructor(serviceWorkerRefArg: ServiceWorker) {
|
||||
this.serviceworkerRef = serviceWorkerRefArg;
|
||||
}
|
||||
|
||||
@ -59,8 +60,8 @@ export class UpdateManager {
|
||||
*/
|
||||
public async getVersionInfoFromServer() {
|
||||
const getAppHashRequest = new plugins.typedrequest.TypedRequest<
|
||||
plugins.lointServiceworker.IRequest_Serviceworker_Backend_VersionInfo
|
||||
>('/lsw-typedrequest', 'serviceworker_versionInfo');
|
||||
interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo
|
||||
>('/sw-typedrequest', 'serviceworker_versionInfo');
|
||||
const result = await getAppHashRequest.fire({});
|
||||
return result;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// types
|
||||
import type * as interfaces from './interfaces/index.js';
|
||||
import type * as interfaces from '../dist_ts_interfaces/index.js';
|
||||
export type {
|
||||
interfaces
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
import * as plugins from '../lele-serviceworker.plugins.js';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as plugins from './lele-serviceworker.plugins.js';
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
import * as interfaces from '../dist_ts_interfaces/index.js';
|
||||
import { logger } from './serviceworker.logging.js';
|
||||
|
||||
/**
|
||||
@ -12,7 +12,7 @@ export class ActionManager {
|
||||
|
||||
constructor() {
|
||||
// lets define handlers on the client/tab side
|
||||
this.deesComms.createTypedHandler<interfaces.IMessage_Serviceworker_Client_UpdateInfo>('serviceworker_newVersion', async req => {
|
||||
this.deesComms.createTypedHandler<interfaces.serviceworker.IMessage_Serviceworker_Client_UpdateInfo>('serviceworker_newVersion', async req => {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 200);
|
||||
@ -22,7 +22,7 @@ export class ActionManager {
|
||||
|
||||
public async waitForServiceWorkerConnection () {
|
||||
console.log('waiting for service worker connection...')
|
||||
const tr = this.deesComms.createTypedRequest<interfaces.IRequest_Client_Serviceworker_ConnectionPolling>('broadcastConnectionPolling');
|
||||
const tr = this.deesComms.createTypedRequest<interfaces.serviceworker.IRequest_Client_Serviceworker_ConnectionPolling>('broadcastConnectionPolling');
|
||||
let connected = false;
|
||||
while (!connected) {
|
||||
tr.fire({
|
||||
@ -45,13 +45,13 @@ export class ActionManager {
|
||||
}
|
||||
|
||||
public async purgeServiceWorkerCache () {
|
||||
const tr = this.deesComms.createTypedRequest<interfaces.IRequest_PurgeServiceWorkerCache>('purgeServiceWorkerCache');
|
||||
const tr = this.deesComms.createTypedRequest<interfaces.serviceworker.IRequest_PurgeServiceWorkerCache>('purgeServiceWorkerCache');
|
||||
const response = await tr.fire({});
|
||||
return response;
|
||||
}
|
||||
|
||||
public async getVersionInfo () {
|
||||
const tr = this.deesComms.createTypedRequest<interfaces.IRequest_Serviceworker_Backend_VersionInfo>('serviceworker_versionInfo');
|
||||
const tr = this.deesComms.createTypedRequest<interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo>('serviceworker_versionInfo');
|
||||
const response = await tr.fire({});
|
||||
return response;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as plugins from './lele-serviceworker.plugins.js';
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
import * as interfaces from '../dist_ts_interfaces/index.js';
|
||||
import { logger } from "./serviceworker.logging.js";
|
||||
|
||||
export class NotificationManager {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as plugins from './lele-serviceworker.plugins.js';
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
import * as interfaces from '../dist_ts_interfaces/index.js';
|
||||
import { logger } from "./serviceworker.logging.js";
|
||||
import { NotificationManager } from './lele-serviceworker.classes.notificationmanager.js';
|
||||
import { ActionManager } from './lele-serviceworker.classes.actionmanager.js';
|
||||
|
Reference in New Issue
Block a user