fix(core): update

This commit is contained in:
Philipp Kunz 2024-05-13 23:24:08 +02:00
parent 84e214d087
commit 1644cbbfad
37 changed files with 880 additions and 7 deletions

View File

@ -58,6 +58,7 @@
"@api.global/typedrequest": "^3.0.23",
"@api.global/typedrequest-interfaces": "^3.0.19",
"@api.global/typedsocket": "^3.0.1",
"@cloudflare/workers-types": "^4.20240502.0",
"@design.estate/dees-comms": "^1.0.24",
"@push.rocks/lik": "^6.0.15",
"@push.rocks/smartchok": "^1.0.34",
@ -68,7 +69,9 @@
"@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/smartopen": "^2.0.0",
"@push.rocks/smartpath": "^5.0.18",

View File

@ -14,6 +14,9 @@ dependencies:
'@api.global/typedsocket':
specifier: ^3.0.1
version: 3.0.1
'@cloudflare/workers-types':
specifier: ^4.20240502.0
version: 4.20240502.0
'@design.estate/dees-comms':
specifier: ^1.0.24
version: 1.0.24
@ -44,9 +47,15 @@ dependencies:
'@push.rocks/smartlog-destination-devtools':
specifier: ^1.0.10
version: 1.0.10
'@push.rocks/smartlog-interfaces':
specifier: ^3.0.0
version: 3.0.0
'@push.rocks/smartmanifest':
specifier: ^2.0.2
version: 2.0.2
'@push.rocks/smartmatch':
specifier: ^2.0.0
version: 2.0.0
'@push.rocks/smartmime':
specifier: ^1.0.5
version: 1.0.6
@ -232,6 +241,10 @@ packages:
regenerator-runtime: 0.14.1
dev: false
/@cloudflare/workers-types@4.20240502.0:
resolution: {integrity: sha512-OB1jIyPOzyOcuZFHWhsQnkRLN6u8+jmU9X3T4KZlGgn3Ivw8pBiswhLOp+yFeChR3Y4/5+V0hPFRko5SReordg==}
dev: false
/@cspotcode/source-map-support@0.8.1:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@api.global/typedserver',
version: '3.0.31',
version: '3.0.32',
description: 'A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.'
}

View File

@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: 'cloudflare-workers',
version: '1.0.192',
description: 'cloudflare-workers'
}

View 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();
}
}

View 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),
});
}
}
}

View 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;
}
}
}

View 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);
};
}

View 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();

View 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;
}
}
}

View 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()
});
}
}

View 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'
];

View File

@ -0,0 +1,7 @@
import * as interfaces from '../interfaces/index.js';
export const instructionObject: { [key: string]: interfaces.IResponderInstruction } = {
'*/ads.txt': {
type: 'ads.txt',
}
};

View File

@ -0,0 +1,2 @@
export * from './botuseragents.js';
export * from './domaininstructions.js';

View 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!');
}
};

View File

@ -0,0 +1 @@
export * from './checks.js';

1
ts_edgeworker/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './classes.edgeworker.js';

View 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>;

View File

@ -0,0 +1 @@
export * from './custom.js';

21
ts_edgeworker/plugins.ts Normal file
View 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
}

View 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);
}
};

View 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);
};

View 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);
};

View 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);
}
};

View 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';

View 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();
};

View 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);
};

View 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,
})
);
}
};

View 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));
}
};

View 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));
}
};

View 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);
}
};

View 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);
}
};

View 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));
}
};

View File

@ -0,0 +1,7 @@
import * as interfaces from './interfaces/index.js';
export class VersionHandler {
}
export const versionHandlerInstance = new VersionHandler();

View File

@ -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 {
// 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(),

View File

@ -1,11 +1,12 @@
import * as plugins from './plugins.js';
import * as interfaces from '../dist_ts_interfaces/index.js';
import { LosslessServiceWorker } 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;
@ -59,7 +60,7 @@ export class UpdateManager {
*/
public async getVersionInfoFromServer() {
const getAppHashRequest = new plugins.typedrequest.TypedRequest<
plugins.lointServiceworker.IRequest_Serviceworker_Backend_VersionInfo
interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo
>('/lsw-typedrequest', 'serviceworker_versionInfo');
const result = await getAppHashRequest.fire({});
return result;

View File

@ -1,2 +0,0 @@
import * as plugins from '../lele-serviceworker.plugins.js';