From 065a253b3e62cabefecec805aa95f87be6607776 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Thu, 4 Dec 2025 17:12:52 +0000 Subject: [PATCH] feat(typedserver): Remove legacy Express-based servertools, drop express deps, and refactor TypedServer to SmartServe + typedrouter with CORS support --- .serena/.gitignore | 1 - .serena/project.yml | 68 ---- changelog.md | 10 + package.json | 6 - pnpm-lock.yaml | 15 - ts/00_commitinfo_data.ts | 2 +- ts/classes.typedserver.ts | 38 ++ ts/index.ts | 8 - ts/plugins.ts | 9 - ts/servertools/classes.compressor.ts | 131 ------- ts/servertools/classes.feed.ts | 35 -- ts/servertools/classes.handler.ts | 17 - ts/servertools/classes.handlerproxy.ts | 132 ------- ts/servertools/classes.handlerstatic.ts | 168 --------- ts/servertools/classes.handlertypedrouter.ts | 19 - ts/servertools/classes.route.ts | 37 -- ts/servertools/classes.server.ts | 345 ------------------- ts/servertools/classes.sitemap.ts | 68 ---- ts/servertools/index.ts | 22 -- ts/servertools/tools.manifest.ts | 14 - ts/servertools/tools.robots.ts | 33 -- ts/servertools/tools.serviceworker.ts | 134 ------- ts/servertools/tools.sslredirect.ts | 22 -- ts/utilityservers/classes.websiteserver.ts | 11 +- 24 files changed, 55 insertions(+), 1290 deletions(-) delete mode 100644 .serena/.gitignore delete mode 100644 .serena/project.yml delete mode 100644 ts/servertools/classes.compressor.ts delete mode 100644 ts/servertools/classes.feed.ts delete mode 100644 ts/servertools/classes.handler.ts delete mode 100644 ts/servertools/classes.handlerproxy.ts delete mode 100644 ts/servertools/classes.handlerstatic.ts delete mode 100644 ts/servertools/classes.handlertypedrouter.ts delete mode 100644 ts/servertools/classes.route.ts delete mode 100644 ts/servertools/classes.server.ts delete mode 100644 ts/servertools/classes.sitemap.ts delete mode 100644 ts/servertools/index.ts delete mode 100644 ts/servertools/tools.manifest.ts delete mode 100644 ts/servertools/tools.robots.ts delete mode 100644 ts/servertools/tools.serviceworker.ts delete mode 100644 ts/servertools/tools.sslredirect.ts diff --git a/.serena/.gitignore b/.serena/.gitignore deleted file mode 100644 index 14d86ad..0000000 --- a/.serena/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/cache diff --git a/.serena/project.yml b/.serena/project.yml deleted file mode 100644 index 4c68b41..0000000 --- a/.serena/project.yml +++ /dev/null @@ -1,68 +0,0 @@ -# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby) -# * For C, use cpp -# * For JavaScript, use typescript -# Special requirements: -# * csharp: Requires the presence of a .sln file in the project folder. -language: typescript - -# whether to use the project's gitignore file to ignore files -# Added on 2025-04-07 -ignore_all_files_in_gitignore: true -# list of additional paths to ignore -# same syntax as gitignore, so you can use * and ** -# Was previously called `ignored_dirs`, please update your config if you are using that. -# Added (renamed) on 2025-04-07 -ignored_paths: [] - -# whether the project is in read-only mode -# If set to true, all editing tools will be disabled and attempts to use them will result in an error -# Added on 2025-04-18 -read_only: false - - -# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. -# Below is the complete list of tools for convenience. -# To make sure you have the latest list of tools, and to view their descriptions, -# execute `uv run scripts/print_tool_overview.py`. -# -# * `activate_project`: Activates a project by name. -# * `check_onboarding_performed`: Checks whether project onboarding was already performed. -# * `create_text_file`: Creates/overwrites a file in the project directory. -# * `delete_lines`: Deletes a range of lines within a file. -# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. -# * `execute_shell_command`: Executes a shell command. -# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. -# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). -# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). -# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. -# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. -# * `initial_instructions`: Gets the initial instructions for the current project. -# Should only be used in settings where the system prompt cannot be set, -# e.g. in clients you have no control over, like Claude Desktop. -# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. -# * `insert_at_line`: Inserts content at a given line in a file. -# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. -# * `list_dir`: Lists files and directories in the given directory (optionally with recursion). -# * `list_memories`: Lists memories in Serena's project-specific memory store. -# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). -# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). -# * `read_file`: Reads a file within the project directory. -# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. -# * `remove_project`: Removes a project from the Serena configuration. -# * `replace_lines`: Replaces a range of lines within a file with new content. -# * `replace_symbol_body`: Replaces the full definition of a symbol. -# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. -# * `search_for_pattern`: Performs a search for a pattern in the project. -# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. -# * `switch_modes`: Activates modes by providing a list of their names -# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. -# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. -# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. -# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. -excluded_tools: [] - -# initial prompt for the project. It will always be given to the LLM upon activating the project -# (contrary to the memories, which are loaded on demand). -initial_prompt: "" - -project_name: "typedserver" diff --git a/changelog.md b/changelog.md index ba4d0c7..999a0db 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 2025-12-04 - 7.6.0 - feat(typedserver) +Remove legacy Express-based servertools, drop express deps, and refactor TypedServer to SmartServe + typedrouter with CORS support + +- Remove legacy ts/servertools module and many Express-based helpers (classes.server, handler, handlerstatic, handlerproxy, compressor, sitemap, feed, tools.*). The servertools compatibility layer is no longer available. +- Drop express-related dependencies from package.json (@types/express, express, body-parser, cors, express-force-ssl). +- Refactor core API: ts/index.ts no longer exports servertools and ts/plugins.ts no longer re-exports Express middleware — consumers must migrate to SmartServe/typedrequest/typedsocket primitives. +- TypedServer rewritten: integrates with @push.rocks/smartserve ControllerRegistry, adds custom route parsing, CORS header helper and OPTIONS preflight handling, improved static file handling with optional reload injection, file watching, typedrouter and typedsocket integration. +- UtilityWebsiteServer now registers the serviceworker versionInfo handler on typedrouter instead of using the removed servertools.serviceworker helper. +- This is a breaking change — public APIs and dependency surface changed; bump major version. + ## 2025-12-04 - 7.5.0 - feat(serviceworker) Add real-time service worker push updates and DeesComms integration (metrics, events, resource caching) diff --git a/package.json b/package.json index 350c5d0..a08193d 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "HTTP server", "SSL", "cors", - "express middleware", "proxy", "sitemap", "feeds", @@ -92,11 +91,6 @@ "@push.rocks/webrequest": "^4.0.1", "@push.rocks/webstore": "^2.0.20", "@tsclass/tsclass": "^9.3.0", - "@types/express": "^5.0.6", - "body-parser": "^2.2.1", - "cors": "^2.8.5", - "express": "^5.2.1", - "express-force-ssl": "^0.3.2", "lit": "^3.3.1" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f2d145..f6c9648 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,21 +107,6 @@ importers: '@tsclass/tsclass': specifier: ^9.3.0 version: 9.3.0 - '@types/express': - specifier: ^5.0.6 - version: 5.0.6 - body-parser: - specifier: ^2.2.1 - version: 2.2.1 - cors: - specifier: ^2.8.5 - version: 2.8.5 - express: - specifier: ^5.2.1 - version: 5.2.1 - express-force-ssl: - specifier: ^0.3.2 - version: 0.3.2 lit: specifier: ^3.3.1 version: 3.3.1 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 64b29e3..a1bd978 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@api.global/typedserver', - version: '7.5.0', + version: '7.6.0', description: 'A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.' } diff --git a/ts/classes.typedserver.ts b/ts/classes.typedserver.ts index 0772a74..5ace0bd 100644 --- a/ts/classes.typedserver.ts +++ b/ts/classes.typedserver.ts @@ -381,6 +381,25 @@ export class TypedServer { }; } + /** + * Add CORS headers to a response + */ + private addCorsHeaders(response: Response): Response { + if (!this.options.cors) return response; + + const headers = new Headers(response.headers); + headers.set('Access-Control-Allow-Origin', '*'); + headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH'); + headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With'); + headers.set('Access-Control-Max-Age', '86400'); + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers + }); + } + /** * Main request handler - routes to appropriate sub-handlers */ @@ -389,6 +408,25 @@ export class TypedServer { const path = url.pathname; const method = request.method.toUpperCase() as THttpMethod; + // Handle OPTIONS preflight for CORS + if (method === 'OPTIONS' && this.options.cors) { + return this.addCorsHeaders(new Response(null, { status: 204 })); + } + + // Process the request and wrap response with CORS headers + const response = await this.handleRequestInternal(request, url, path, method); + return this.addCorsHeaders(response); + } + + /** + * Internal request handler - routes to appropriate sub-handlers + */ + private async handleRequestInternal( + request: Request, + url: URL, + path: string, + method: THttpMethod + ): Promise { // First, try to match via ControllerRegistry (decorated routes) const match = plugins.smartserve.ControllerRegistry.matchRoute(path, method); if (match) { diff --git a/ts/index.ts b/ts/index.ts index 39a1f7f..55bdffb 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -1,15 +1,7 @@ import * as plugins from './plugins.js'; -import * as servertools from './servertools/index.js'; - -export { servertools }; - export * from './classes.typedserver.js'; -// Type helpers - using native Web API Request/Response types -// Native Request and Response are available in Node.js 18+ and all modern browsers -// Legacy Express types are available via servertools for backward compatibility - // lets export utilityservers import * as utilityservers from './utilityservers/index.js'; export { utilityservers }; diff --git a/ts/plugins.ts b/ts/plugins.ts index f29eb18..3c9cab0 100644 --- a/ts/plugins.ts +++ b/ts/plugins.ts @@ -65,12 +65,3 @@ export const fsInstance = new smartfs.SmartFs(new smartfs.SmartFsProviderNode()) import * as smartserve from '@push.rocks/smartserve'; export { smartserve }; - -// Legacy Express dependencies - kept for backward compatibility with deprecated servertools -// These will be removed in the next major version -import express from 'express'; -import bodyParser from 'body-parser'; -import cors from 'cors'; -import expressForceSsl from 'express-force-ssl'; - -export { express, bodyParser, cors, expressForceSsl }; diff --git a/ts/servertools/classes.compressor.ts b/ts/servertools/classes.compressor.ts deleted file mode 100644 index 2c510c9..0000000 --- a/ts/servertools/classes.compressor.ts +++ /dev/null @@ -1,131 +0,0 @@ -import * as plugins from '../plugins.js'; - -export type TCompressionMethod = 'gzip' | 'deflate' | 'br' | 'none'; -export interface ICompressionResult { - result: Buffer; - compressionMethod: TCompressionMethod; -} - -export class Compressor { - private _cache: Map; - private MAX_CACHE_SIZE: number = 100 * 1024 * 1024; // 100 MB - - constructor() { - this._cache = new Map(); - } - - private _addToCache(key: string, value: Buffer) { - this._cache.set(key, value); - this._manageCacheSize(); - } - - private _manageCacheSize() { - let currentSize = Array.from(this._cache.values()).reduce((acc, buffer) => acc + buffer.length, 0); - - while (currentSize > this.MAX_CACHE_SIZE) { - const firstKey = this._cache.keys().next().value; - const firstValue = this._cache.get(firstKey)!; - currentSize -= firstValue.length; - this._cache.delete(firstKey); - } - } - - public async compressContent( - content: Buffer, - method: 'gzip' | 'deflate' | 'br' | 'none' - ): Promise { - const cacheKey = content.toString('base64') + method; - const cachedResult = this._cache.get(cacheKey); - - if (cachedResult) { - return cachedResult; - } - - return new Promise((resolve, reject) => { - const callback = (err: Error | null, result: Buffer) => { - if (err) reject(err); - else { - this._addToCache(cacheKey, result); - resolve(result); - } - }; - - switch (method) { - case 'gzip': - plugins.zlib.gzip(content, { - level: 1, - },callback,); - break; - case 'br': - plugins.zlib.brotliCompress(content, {}, callback); - break; - case 'deflate': - plugins.zlib.deflate(content, callback); - break; - default: - this._addToCache(cacheKey, content); - resolve(content); - } - }); - } - - public determineCompression(acceptEncoding: string | string[], preferredCompressionMethodsArg: TCompressionMethod[] = []) { - // Ensure acceptEncoding is a single string - const encodingString = Array.isArray(acceptEncoding) - ? acceptEncoding.join(', ') - : acceptEncoding; - - let compressionMethod: TCompressionMethod = 'none'; - - // Prioritize preferred compression methods if provided - for (const preferredMethod of preferredCompressionMethodsArg) { - if (new RegExp(`\\b${preferredMethod}\\b`).test(encodingString)) { - return preferredMethod; - } - } - - // Fallback to default prioritization if no preferred method matches - if (/\bbr\b/.test(encodingString)) { - compressionMethod = 'br'; - } else if (/\bgzip\b/.test(encodingString)) { - compressionMethod = 'gzip'; - } else if (/\bdeflate\b/.test(encodingString)) { - compressionMethod = 'deflate'; - } - - return compressionMethod; - } - - public async maybeCompress(requestHeaders: plugins.http.IncomingHttpHeaders, content: Buffer, preferredCompressionMethodsArg?: TCompressionMethod[]): Promise { - const acceptEncoding = requestHeaders['accept-encoding']; - const compressionMethod = this.determineCompression(acceptEncoding, preferredCompressionMethodsArg); - const result = await this.compressContent(content, compressionMethod); - return { - result, - compressionMethod, - }; - } - - public createCompressionStream(method: 'gzip' | 'deflate' | 'br' | 'none') { - let compressionStream: any; - switch (method) { - case 'gzip': - compressionStream = plugins.zlib.createGzip(); - return compressionStream; - case 'br': - compressionStream = plugins.zlib.createBrotliCompress({ - chunkSize: 16 * 1024, - params: { - - }, - }); - return compressionStream; - case 'deflate': - compressionStream = plugins.zlib.createDeflate(); - return compressionStream; - default: - compressionStream = plugins.smartstream.createPassThrough(); - return compressionStream; - } - } -} \ No newline at end of file diff --git a/ts/servertools/classes.feed.ts b/ts/servertools/classes.feed.ts deleted file mode 100644 index a871a41..0000000 --- a/ts/servertools/classes.feed.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Handler } from './classes.handler.js'; -import { Server } from './classes.server.js'; -import * as plugins from '../plugins.js'; - -export class Feed { - public smartexpressRef: Server; - public smartfeedInstance = new plugins.smartfeed.Smartfeed(); - - public feedHandler = new Handler('GET', async (req, res) => { - if (!this.smartexpressRef.options.feedMetadata) { - res.status(500); - res.write('feed metadata is missing'); - res.end(); - return; - } - if (!this.smartexpressRef.options.articleGetterFunction) { - res.status(500); - res.write('no article getter function defined.'); - res.end(); - return; - } - const xmlString = await this.smartfeedInstance.createFeedFromArticleArray( - this.smartexpressRef.options.feedMetadata, - await this.smartexpressRef.options.articleGetterFunction() - ); - res.type('.xml'); - res.write(xmlString); - res.end(); - }); - - constructor(smartexpressRefArg: Server) { - this.smartexpressRef = smartexpressRefArg; - this.smartexpressRef.addRouteBefore('/feed', this.feedHandler); - } -} diff --git a/ts/servertools/classes.handler.ts b/ts/servertools/classes.handler.ts deleted file mode 100644 index 37f7f78..0000000 --- a/ts/servertools/classes.handler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as plugins from '../plugins.js'; -import { type Request, type Response } from 'express'; - -export interface IHandlerFunction { - (requestArg: Request, responseArg: Response): void; -} - -export type THttpMethods = 'ALL' | 'GET' | 'POST' | 'PUT' | 'DELETE'; - -export class Handler { - httpMethod: THttpMethods; - handlerFunction: IHandlerFunction; - constructor(httpMethodArg: THttpMethods, handlerArg: IHandlerFunction) { - this.httpMethod = httpMethodArg; - this.handlerFunction = handlerArg; - } -} diff --git a/ts/servertools/classes.handlerproxy.ts b/ts/servertools/classes.handlerproxy.ts deleted file mode 100644 index 26d8928..0000000 --- a/ts/servertools/classes.handlerproxy.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as plugins from '../plugins.js'; -import { Handler } from './classes.handler.js'; - -import * as interfaces from '../../dist_ts_interfaces/index.js'; - -export class HandlerProxy extends Handler { - /** - * The constuctor of HandlerProxy - * @param remoteMountPointArg - */ - constructor( - remoteMountPointArg: string, - optionsArg?: { - responseModifier?: interfaces.TResponseModifier; - headers?: { [key: string]: string }; - } - ) { - super('ALL', async (req, res) => { - // Extract the path using Express 5's params or fallback methods - let relativeRequestPath: string; - if (req.params && req.params.splat !== undefined) { - // Express 5 wildcard route (/*splat or /{*splat}) - // Handle array values - join them if array, otherwise use as-is - relativeRequestPath = Array.isArray(req.params.splat) ? req.params.splat.join('/') : String(req.params.splat || ''); - } else if (req.params && req.params[0] !== undefined) { - // Numbered parameter fallback - relativeRequestPath = Array.isArray(req.params[0]) ? req.params[0].join('/') : String(req.params[0] || ''); - } else if (req.baseUrl) { - // If there's a baseUrl, remove it from the path - relativeRequestPath = req.path.slice(req.baseUrl.length); - } else if (req.route && req.route.path === '/') { - // Root route - use full path minus leading slash - relativeRequestPath = req.path.slice(1); - } else { - // Fallback to the original slicing logic for compatibility - relativeRequestPath = req.path.slice(req.route.path.length - 1); - } - - // Ensure relativeRequestPath is a string and has no leading slash - relativeRequestPath = String(relativeRequestPath || ''); - if (relativeRequestPath.startsWith('/')) { - relativeRequestPath = relativeRequestPath.slice(1); - } - const proxyRequestUrl = remoteMountPointArg + relativeRequestPath; - console.log(`proxy ${req.path} to ${proxyRequestUrl}`); - let proxiedResponse: plugins.smartrequest.ICoreResponse; - try { - const smartRequest = plugins.smartrequest.SmartRequest.create() - .url(proxyRequestUrl); - - // Execute request based on method - switch (req.method.toUpperCase()) { - case 'GET': - proxiedResponse = await smartRequest.get(); - break; - case 'POST': - proxiedResponse = await smartRequest.post(); - break; - case 'PUT': - proxiedResponse = await smartRequest.put(); - break; - case 'DELETE': - proxiedResponse = await smartRequest.delete(); - break; - case 'PATCH': - proxiedResponse = await smartRequest.patch(); - break; - default: - // For other methods, default to GET - proxiedResponse = await smartRequest.get(); - break; - } - } catch { - res.end('failed to fullfill request'); - return; - } - const headers = proxiedResponse.headers; - for (const header of Object.keys(headers)) { - res.set(header, headers[header] as string); - } - - // set additional headers - if (optionsArg && optionsArg.headers) { - for (const key of Object.keys(optionsArg.headers)) { - res.set(key, optionsArg.headers[key]); - } - } - - // Get response body as buffer - let responseToSend: Buffer; - try { - const arrayBuffer = await proxiedResponse.arrayBuffer(); - responseToSend = Buffer.from(arrayBuffer); - } catch { - // If we can't get arrayBuffer, try text - try { - const text = await proxiedResponse.text(); - responseToSend = Buffer.from(text); - } catch { - // Provide a default empty buffer if body cannot be read - responseToSend = Buffer.from(''); - } - } - - if (optionsArg && optionsArg.responseModifier) { - const modifiedResponse = await optionsArg.responseModifier({ - headers: res.getHeaders(), - path: req.path, - responseContent: responseToSend, - }); - - // headers - for (const key of Object.keys(res.getHeaders())) { - if (!modifiedResponse.headers[key]) { - res.removeHeader(key); - } - } - - for (const key of Object.keys(modifiedResponse.headers)) { - res.setHeader(key, modifiedResponse.headers[key]); - } - - // responseContent - responseToSend = modifiedResponse.responseContent; - } - - res.status(200); - res.write(responseToSend); - res.end(); - }); - } -} \ No newline at end of file diff --git a/ts/servertools/classes.handlerstatic.ts b/ts/servertools/classes.handlerstatic.ts deleted file mode 100644 index 3995c86..0000000 --- a/ts/servertools/classes.handlerstatic.ts +++ /dev/null @@ -1,168 +0,0 @@ -import * as plugins from '../plugins.js'; -import * as interfaces from '../../dist_ts_interfaces/index.js'; - -import { Handler } from './classes.handler.js'; -import { Compressor, type TCompressionMethod, type ICompressionResult } from './classes.compressor.js'; - -export class HandlerStatic extends Handler { - public compressor = new Compressor(); - constructor( - pathArg: string, - optionsArg?: { - requestModifier?: interfaces.TRequestModifier; - responseModifier?: interfaces.TResponseModifier; - headers?: { [key: string]: string }; - serveIndexHtmlDefault?: boolean; - enableCompression?: boolean; - preferredCompressionMethod?: TCompressionMethod; - } - ) { - super('GET', async (req, res) => { - let requestPath = req.path; - let requestHeaders = req.headers; - let requestBody = req.body; - let travelData: unknown; - if (optionsArg && optionsArg.requestModifier) { - const modifiedRequest = await optionsArg.requestModifier({ - headers: requestHeaders, - path: requestPath, - body: requestBody, - }); - - requestHeaders = modifiedRequest.headers; - requestPath = modifiedRequest.path; - requestBody = modifiedRequest.body; - travelData = modifiedRequest.travelData; - } - - // lets compute some paths - // Extract the path using Express 5's params or fallback methods - let filePath: string; - if (req.params && req.params.splat !== undefined) { - // Express 5 wildcard route (/*splat or /{*splat}) - // Handle array values - join them if array, otherwise use as-is - filePath = Array.isArray(req.params.splat) ? req.params.splat.join('/') : String(req.params.splat || ''); - } else if (req.params && req.params[0] !== undefined) { - // Numbered parameter fallback - filePath = Array.isArray(req.params[0]) ? req.params[0].join('/') : String(req.params[0] || ''); - } else if (req.baseUrl) { - // If there's a baseUrl, remove it from the path - filePath = requestPath.slice(req.baseUrl.length); - } else if (req.route && req.route.path === '/') { - // Root route - use full path minus leading slash - filePath = requestPath.slice(1); - } else { - // Fallback to the original slicing logic for compatibility - filePath = requestPath.slice(req.route.path.length - 1); - } - - // Ensure filePath is a string and has no leading slash - filePath = String(filePath || ''); - if (filePath.startsWith('/')) { - filePath = filePath.slice(1); - } - if (requestPath === '') { - console.log('replaced root with index.html'); - filePath = 'index.html'; - } - console.log(filePath); - const joinedPath = plugins.path.join(pathArg, filePath); - const defaultPath = plugins.path.join(pathArg, 'index.html'); - let parsedPath = plugins.path.parse(joinedPath); - let usedPath: string; - - // important security checks - if ( - requestPath.includes('..') || // don't allow going up the filePath - requestPath.includes('~') || // don't allow referencing of home directory - !joinedPath.startsWith(pathArg) // make sure the joined path is within the directory - ) { - res.writeHead(500); - res.end(); - return; - } - - // set additional headers - if (optionsArg && optionsArg.headers) { - for (const key of Object.keys(optionsArg.headers)) { - res.set(key, optionsArg.headers[key]); - } - } - - // lets actually care about serving, if security checks pass - let fileBuffer: Buffer; - try { - fileBuffer = await plugins.fsInstance.file(joinedPath).read() as Buffer; - usedPath = joinedPath; - } catch (err) { - // try serving index.html instead - console.log(`could not resolve ${joinedPath}`); - if (optionsArg && optionsArg.serveIndexHtmlDefault) { - console.log(`serving default path ${defaultPath} instead of ${joinedPath}`); - try { - parsedPath = plugins.path.parse(defaultPath); - fileBuffer = await plugins.fsInstance.file(defaultPath).read() as Buffer; - usedPath = defaultPath; - } catch (err) { - res.writeHead(500); - res.end('File not found!'); - return; - } - } else { - res.writeHead(500); - res.end('File not found!'); - return; - } - } - - res.type(parsedPath.ext); - - const headers = res.getHeaders(); - - // lets modify the response at last - if (optionsArg && optionsArg.responseModifier) { - const modifiedResponse = await optionsArg.responseModifier({ - headers: res.getHeaders(), - path: usedPath, - responseContent: fileBuffer, - travelData, - }); - - // headers - for (const key of Object.keys(res.getHeaders())) { - if (!modifiedResponse.headers[key]) { - res.removeHeader(key); - } - } - - for (const key of Object.keys(modifiedResponse.headers)) { - res.setHeader(key, modifiedResponse.headers[key]); - } - - // responseContent - fileBuffer = modifiedResponse.responseContent; - } - - // lets finally deal with compression - let compressionResult: ICompressionResult; - - if (optionsArg && optionsArg.enableCompression) { - compressionResult = await this.compressor.maybeCompress(requestHeaders, fileBuffer, [optionsArg.preferredCompressionMethod]); - } else { - compressionResult = { - compressionMethod: 'none', - result: fileBuffer, - }; - } - - res.status(200); - if (compressionResult?.compressionMethod) { - res.header('Content-Encoding', compressionResult.compressionMethod); - res.write(compressionResult.result); - } else { - res.write(fileBuffer); - } - res.end(); - }); - } -} diff --git a/ts/servertools/classes.handlertypedrouter.ts b/ts/servertools/classes.handlertypedrouter.ts deleted file mode 100644 index db55dc5..0000000 --- a/ts/servertools/classes.handlertypedrouter.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as plugins from '../plugins.js'; -import { Handler } from './classes.handler.js'; - -import * as interfaces from '../../dist_ts_interfaces/index.js'; - -export class HandlerTypedRouter extends Handler { - /** - * The constuctor of HandlerProxy - * @param remoteMountPointArg - */ - constructor(typedrouter: plugins.typedrequest.TypedRouter) { - super('POST', async (req, res) => { - const response = await typedrouter.routeAndAddResponse(req.body); - res.type('json'); - res.write(plugins.smartjson.stringify(response)); - res.end(); - }); - } -} diff --git a/ts/servertools/classes.route.ts b/ts/servertools/classes.route.ts deleted file mode 100644 index f413b77..0000000 --- a/ts/servertools/classes.route.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as plugins from '../plugins.js'; -import { Handler } from './classes.handler.js'; -import { Server } from './classes.server.js'; - -import { type IRoute as IExpressRoute } from 'express'; - -export class Route { - public routeString: string; - - /** - * an object map of handlers - * Why multiple? Because GET, POST, PUT, DELETE, etc. can all have different handlers - */ - public handlerObjectMap = new plugins.lik.ObjectMap(); - - public expressMiddlewareObjectMap = new plugins.lik.ObjectMap(); - public expressRoute: IExpressRoute; // will be set to server route on server start - constructor(ServerArg: Server, routeStringArg: string) { - this.routeString = routeStringArg; - } - - /** - * add a handler to do something with requests - * @param handlerArg - */ - public addHandler(handlerArg: Handler) { - this.handlerObjectMap.add(handlerArg); - } - - /** - * add a express middleware - * @param middlewareArg - */ - public addExpressMiddleWare(middlewareArg: plugins.express.Application) { - this.expressMiddlewareObjectMap.add(middlewareArg); - } -} diff --git a/ts/servertools/classes.server.ts b/ts/servertools/classes.server.ts deleted file mode 100644 index 966422b..0000000 --- a/ts/servertools/classes.server.ts +++ /dev/null @@ -1,345 +0,0 @@ -import * as plugins from '../plugins.js'; - -import { Route } from './classes.route.js'; -import { Handler } from './classes.handler.js'; -import { HandlerTypedRouter } from './classes.handlertypedrouter.js'; - -// export types -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 '../classes.typedserver.js'; -export type TServerStatus = 'initiated' | 'running' | 'stopped'; - -/** - * can be used to spawn a server to answer http/https calls - * for constructor options see [[IServerOptions]] - */ -export class Server { - public httpServer: plugins.http.Server | plugins.https.Server; - public expressAppInstance: plugins.express.Application; - public routeObjectMap = new Array(); - public options: IServerOptions; - public serverStatus: TServerStatus = 'initiated'; - - public feed: Feed; - public sitemap: Sitemap; - - public executeAfterStartFunctions: (() => Promise)[] = []; - - // do stuff when server is ready - private startedDeferred = plugins.smartpromise.defer(); - // tslint:disable-next-line:member-ordering - public startedPromise = this.startedDeferred.promise; - - private socketMap = new plugins.lik.ObjectMap(); - - constructor(optionsArg: IServerOptions) { - this.options = { - ...optionsArg, - }; - } - - /** - * allows updating of server options - * @param optionsArg - */ - public updateServerOptions(optionsArg: IServerOptions) { - Object.assign(this.options, optionsArg); - } - - public addTypedRequest(typedrouter: plugins.typedrequest.TypedRouter) { - this.addRoute('/typedrequest', new HandlerTypedRouter(typedrouter)); - } - - /** - * @deprecated This method is deprecated. Use TypedServer with SmartServe integration instead. - * TypedSocket v4 no longer supports attaching to an existing Express server. - */ - public addTypedSocket(typedrouter: plugins.typedrequest.TypedRouter): void { - console.warn( - '[DEPRECATED] servertools.Server.addTypedSocket() is deprecated and has no effect. ' + - 'Use TypedServer with SmartServe integration for WebSocket support.' - ); - // TypedSocket v4 creates its own server, which would conflict with Express. - // This method is now a no-op for backward compatibility. - } - - public addRoute(routeStringArg: string, handlerArg?: Handler) { - const route = new Route(this, routeStringArg); - if (handlerArg) { - route.addHandler(handlerArg); - } - this.routeObjectMap.push(route); - return route; - } - - public addRouteBefore(routeStringArg: string, handlerArg?: Handler) { - const route = new Route(this, routeStringArg); - if (handlerArg) { - route.addHandler(handlerArg); - } - this.routeObjectMap.unshift(route); - return route; - } - - /** - * starts the server and sets up the routes - * @param portArg - * @param doListen - */ - public async start(portArg: number | string = this.options.port, doListen = true) { - const done = plugins.smartpromise.defer(); - - if (typeof portArg === 'string') { - portArg = parseInt(portArg); - } - - this.expressAppInstance = plugins.express(); - if (!this.httpServer && (!this.options.privateKey || !this.options.publicKey)) { - console.log('Got no SSL certificates. Please ensure encryption using e.g. a reverse proxy'); - this.httpServer = plugins.http.createServer(this.expressAppInstance); - } else if (!this.httpServer) { - console.log('Got SSL certificate. Using it for the http server'); - this.httpServer = plugins.https.createServer( - { - key: this.options.privateKey, - cert: this.options.publicKey, - }, - this.expressAppInstance - ); - } else { - console.log('Using externally supplied http server'); - } - this.httpServer.keepAliveTimeout = 600 * 1000; - this.httpServer.headersTimeout = 20 * 1000; - - // general request handlling - this.expressAppInstance.use((req, res, next) => { - next(); - }); - - // forceSsl - if (this.options.forceSsl) { - this.expressAppInstance.set('forceSSLOptions', { - enable301Redirects: true, - trustXFPHeader: true, - sslRequiredMessage: 'SSL Required.', - }); - this.expressAppInstance.use(plugins.expressForceSsl); - } - - // cors - if (this.options.cors) { - const cors = plugins.cors({ - allowedHeaders: '*', - methods: '*', - origin: '*', - }); - - this.expressAppInstance.use(cors); - this.expressAppInstance.options('/{*splat}', cors); - } - - this.expressAppInstance.use((req, res, next) => { - res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin'); - res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp'); - res.setHeader('Cross-Origin-Opener-Policy', 'same-origin'); - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('SERVEZONE_ROUTE', 'LOSSLESS_ORIGIN_CONTAINER'); - res.setHeader('Cache-Control', 'no-cache'); - res.setHeader('Expires', new Date(Date.now()).toUTCString()); - next(); - }); - - // body parsing - this.expressAppInstance.use(async (req, res, next) => { - if (req.headers['content-type'] === 'application/json') { - let data = ''; - req.on('data', chunk => { - data += chunk; - }); - req.on('end', () => { - try { - req.body = plugins.smartjson.parse(data); - next(); - } catch (error) { - res.status(400).send('Invalid JSON'); - } - }); - } else { - next(); - } - }); - this.expressAppInstance.use(plugins.bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded - - // robots - if (this.options.robots && this.options.domain) { - await setupRobots(this, this.options.domain); - } - - // manifest.json - if (this.options.manifest) { - await setupManifest(this.expressAppInstance, this.options.manifest); - } - - // sitemaps - if (this.options.sitemap) { - this.sitemap = new Sitemap(this); - } - - if (this.options.feed) { - // feed - this.feed = new Feed(this); - } - - // appVersion - if (this.options.appVersion) { - this.expressAppInstance.use((req, res, next) => { - res.set('appversion', this.options.appVersion); - next(); - }); - this.addRoute( - '/appversion', - new Handler('GET', async (req, res) => { - res.write(this.options.appVersion); - res.end(); - }) - ); - } - - // set up routes in for express - await this.routeObjectMap.forEach(async (routeArg) => { - console.log( - `"${routeArg.routeString}" maps to ${routeArg.handlerObjectMap.getArray().length} handlers` - ); - const expressRoute = this.expressAppInstance.route(routeArg.routeString); - routeArg.handlerObjectMap.forEach(async (handler) => { - console.log(` -> ${handler.httpMethod}`); - switch (handler.httpMethod) { - case 'GET': - expressRoute.get(handler.handlerFunction); - return; - case 'POST': - expressRoute.post(handler.handlerFunction); - return; - case 'PUT': - expressRoute.put(handler.handlerFunction); - return; - case 'ALL': - expressRoute.all(handler.handlerFunction); - return; - case 'DELETE': - expressRoute.delete(handler.handlerFunction); - return; - default: - return; - } - }); - }); - - if (this.options.defaultAnswer) { - this.expressAppInstance.get('/', async (request, response) => { - response.send(await this.options.defaultAnswer()); - }); - } - - this.httpServer.on('connection', (connection: plugins.net.Socket) => { - this.socketMap.add(connection); - console.log(`added connection. now ${this.socketMap.getArray().length} sockets connected.`); - - const closeListener = () => { - console.log('connection closed'); - cleanupConnection(); - }; - - const errorListener = () => { - console.log('connection errored'); - cleanupConnection(); - }; - - const endListener = () => { - console.log('connection ended'); - cleanupConnection(); - }; - - const timeoutListener = () => { - console.log('connection timed out'); - cleanupConnection(); - }; - - connection.addListener('close', closeListener); - connection.addListener('error', errorListener); - connection.addListener('end', endListener); - connection.addListener('timeout', timeoutListener); - - const cleanupConnection = async () => { - connection.removeListener('close', closeListener); - connection.removeListener('error', errorListener); - connection.removeListener('end', endListener); - connection.removeListener('timeout', timeoutListener); - - if (this.socketMap.checkForObject(connection)) { - this.socketMap.remove(connection); - console.log(`removed connection. ${this.socketMap.getArray().length} sockets remaining.`); - await plugins.smartdelay.delayFor(0); - if (connection.destroyed === false) { - connection.destroy(); - } - } - }; - }); - - // finally listen on a port - if (doListen) { - this.httpServer.listen(portArg, '0.0.0.0', () => { - console.log(`now listening on ${portArg}!`); - this.startedDeferred.resolve(); - this.serverStatus = 'running'; - done.resolve(); - }); - } else { - console.log( - 'The server does not listen on a network stack and instead expects to get handed requests by other mechanics' - ); - } - await done.promise; - for (const executeAfterStartFunction of this.executeAfterStartFunctions) { - await executeAfterStartFunction(); - } - } - - public getHttpServer() { - return this.httpServer; - } - - public getExpressAppInstance() { - return this.expressAppInstance; - } - - public async stop() { - const done = plugins.smartpromise.defer(); - if (this.httpServer) { - this.httpServer.close(async () => { - this.serverStatus = 'stopped'; - done.resolve(); - }); - await this.socketMap.forEach(async (socket) => { - socket.destroy(); - }); - } else { - throw new Error('There is no Server to be stopped. Have you started it?'); - } - return await done.promise; - } - - /** - * allows handling requests and responses that come from other - * @param req - * @param res - */ - public async handleReqRes(req: plugins.express.Request, res: plugins.express.Response) { - this.expressAppInstance(req, res); - } -} diff --git a/ts/servertools/classes.sitemap.ts b/ts/servertools/classes.sitemap.ts deleted file mode 100644 index 78cb80d..0000000 --- a/ts/servertools/classes.sitemap.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Server } from './classes.server.js'; -import { Handler } from './classes.handler.js'; -import * as plugins from '../plugins.js'; -import { type IUrlInfo } from '@push.rocks/smartsitemap'; - -export class Sitemap { - public smartexpressRef: Server; - public smartSitemap = new plugins.smartsitemap.SmartSitemap(); - public urls: plugins.smartsitemap.IUrlInfo[] = []; - - /** - * handles the normal sitemap request - */ - public sitemapHandler = new Handler('GET', async (req, res) => { - const sitemapXmlString = await this.smartSitemap.createSitemapFromUrlInfoArray(this.urls); - res.type('.xml'); - res.write(sitemapXmlString); - res.end(); - }); - - /** - * handles the sitemap-news request - */ - public sitemapNewsHandler = new Handler('GET', async (req, res) => { - if (!this.smartexpressRef.options.articleGetterFunction) { - res.status(500); - res.write('no article getter function defined.'); - res.end(); - return; - } - const sitemapNewsXml = await this.smartSitemap.createSitemapNewsFromArticleArray( - await this.smartexpressRef.options.articleGetterFunction() - ); - res.type('.xml'); - res.write(sitemapNewsXml); - res.end(); - }); - - constructor(smartexpressRefArg: Server) { - this.smartexpressRef = smartexpressRefArg; - this.smartexpressRef.addRouteBefore('/sitemap', this.sitemapHandler); - this.smartexpressRef.addRouteBefore('/sitemap-news', this.sitemapNewsHandler); - - // lets set the default url - if (this.smartexpressRef.options.domain) { - this.urls.push({ - url: `https://${this.smartexpressRef.options.domain}/`, - timestamp: Date.now(), - frequency: 'daily', - }); - } - } - - /** - * replaces the current urlsArray - * @param urlsArg - */ - public replaceUrls(urlsArg: IUrlInfo[]) { - this.urls = urlsArg; - } - - /** - * adds urls to the current set of urls - */ - public addUrls(urlsArg: IUrlInfo[]) { - this.urls = this.urls.concat(urlsArg); - } -} \ No newline at end of file diff --git a/ts/servertools/index.ts b/ts/servertools/index.ts deleted file mode 100644 index f27230a..0000000 --- a/ts/servertools/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Core utilities that don't depend on Express -export * from './classes.compressor.js'; - -// Legacy Express-based classes - deprecated, will be removed in next major version -// These are kept for backward compatibility but should not be used for new code -// Use SmartServe decorator-based controllers instead -/** @deprecated Use SmartServe directly */ -export * from './classes.server.js'; -/** @deprecated Use SmartServe @Route decorator */ -export * from './classes.route.js'; -/** @deprecated Use SmartServe @Get/@Post decorators */ -export * from './classes.handler.js'; -/** @deprecated Use SmartServe static file serving */ -export * from './classes.handlerstatic.js'; -/** @deprecated Use SmartServe custom handler */ -export * from './classes.handlerproxy.js'; -/** @deprecated Use SmartServe TypedRouter integration */ -export * from './classes.handlertypedrouter.js'; - -// Service worker utilities - uses legacy patterns, will be migrated -import * as serviceworker from './tools.serviceworker.js'; -export { serviceworker }; diff --git a/ts/servertools/tools.manifest.ts b/ts/servertools/tools.manifest.ts deleted file mode 100644 index 0306f5b..0000000 --- a/ts/servertools/tools.manifest.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as plugins from '../plugins.js'; - -export const setupManifest = async ( - expressInstanceArg: plugins.express.Application, - manifestArg: plugins.smartmanifest.ISmartManifestConstructorOptions -) => { - const smartmanifestInstance = new plugins.smartmanifest.SmartManifest(manifestArg); - expressInstanceArg.get('/manifest.json', async (req, res) => { - res.status(200); - res.type('json'); - res.write(smartmanifestInstance.jsonString()); - res.end(); - }); -}; diff --git a/ts/servertools/tools.robots.ts b/ts/servertools/tools.robots.ts deleted file mode 100644 index 4695fec..0000000 --- a/ts/servertools/tools.robots.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as plugins from '../plugins.js'; -import { Server } from './classes.server.js'; -import { Handler } from './classes.handler.js'; - -export const setupRobots = async (smartexpressRefArg: Server, domainArg: string) => { - smartexpressRefArg.addRouteBefore( - '/robots.txt', - new Handler('GET', async (req, res) => { - res.type('text/plain'); - res.send(` -User-agent: Googlebot-News -Disallow: /account -Disallow: /login - -User-agent: * -Disallow: /account -Disallow: /login - -${ - smartexpressRefArg.options.blockWaybackMachine - ? ` -User-Agent: ia_archiver -Disallow: / -` - : `` -} - -Sitemap: https://${domainArg}/sitemap -Sitemap: https://${domainArg}/sitemap-news -`); - }) - ); -}; diff --git a/ts/servertools/tools.serviceworker.ts b/ts/servertools/tools.serviceworker.ts deleted file mode 100644 index c518b12..0000000 --- a/ts/servertools/tools.serviceworker.ts +++ /dev/null @@ -1,134 +0,0 @@ -import * as plugins from '../plugins.js'; -import * as paths from '../paths.js'; - -import * as interfaces from '../../dist_ts_interfaces/index.js'; -import type { TypedServer } from '../classes.typedserver.js'; - -// Lazy-loaded service worker bundle content -let swBundleJs: string | null = null; -let swBundleJsMap: string | null = null; - -const loadServiceWorkerBundle = async (): Promise => { - if (swBundleJs === null) { - swBundleJs = (await plugins.fsInstance - .file(plugins.path.join(paths.serviceworkerBundleDir, './serviceworker.bundle.js')) - .encoding('utf8') - .read()) as string; - } - if (swBundleJsMap === null) { - swBundleJsMap = (await plugins.fsInstance - .file(plugins.path.join(paths.serviceworkerBundleDir, './serviceworker.bundle.js.map')) - .encoding('utf8') - .read()) as string; - } -}; - -let swVersionInfo: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'] = - null; - -export const addServiceWorkerRoute = ( - typedserverInstance: TypedServer, - swDataFunc: () => interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'] -) => { - // Set the version info - swVersionInfo = swDataFunc(); - - // Handler function for serviceworker bundle requests - const handleServiceWorkerRequest = async (request: Request): Promise => { - await loadServiceWorkerBundle(); - const url = new URL(request.url); - const path = url.pathname; - - if (path === '/serviceworker/serviceworker.bundle.js' || path === '/serviceworker.bundle.js') { - return new Response( - swBundleJs + '\n' + `/** appSemVer: ${swVersionInfo?.appSemVer || 'not set'} */`, - { - status: 200, - headers: { 'Content-Type': 'text/javascript' }, - } - ); - } else if ( - path === '/serviceworker/serviceworker.bundle.js.map' || - path === '/serviceworker.bundle.js.map' - ) { - return new Response(swBundleJsMap, { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } - - return null; - }; - - // Service worker bundle handler - nested path - typedserverInstance.addRoute('/serviceworker/*splat', 'GET', handleServiceWorkerRequest); - - // Service worker bundle handler - root level (for /serviceworker.bundle.js) - typedserverInstance.addRoute('/serviceworker.bundle.js', 'GET', handleServiceWorkerRequest); - typedserverInstance.addRoute('/serviceworker.bundle.js.map', 'GET', handleServiceWorkerRequest); - - // Typed request handler for service worker - typedserverInstance.addRoute('/sw-typedrequest', 'POST', async (request: Request) => { - try { - const body = await request.json(); - - // Create a local typed router for service worker requests - const typedrouter = new plugins.typedrequest.TypedRouter(); - - typedrouter.addTypedHandler( - new plugins.typedrequest.TypedHandler( - 'serviceworker_versionInfo', - async () => { - return swDataFunc(); - } - ) - ); - - // Speedtest handler for measuring connection speed (time-based chunked approach) - typedrouter.addTypedHandler( - new plugins.typedrequest.TypedHandler( - 'serviceworker_speedtest', - async (reqArg) => { - const chunkSizeKB = reqArg.chunkSizeKB || 64; - const sizeBytes = chunkSizeKB * 1024; - let payload: string | undefined; - let bytesTransferred = 0; - - switch (reqArg.type) { - case 'download_chunk': - // Generate chunk payload for download test - payload = 'x'.repeat(sizeBytes); - bytesTransferred = sizeBytes; - break; - case 'upload_chunk': - // For upload, measure bytes received from client - bytesTransferred = reqArg.payload?.length || 0; - break; - case 'latency': - // Simple ping - no payload needed - bytesTransferred = 0; - break; - } - - return { - bytesTransferred, - timestamp: Date.now(), - payload, // Only for download_chunk tests - }; - } - ) - ); - - const response = await typedrouter.routeAndAddResponse(body); - return new Response(plugins.smartjson.stringify(response), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - return new Response(JSON.stringify({ error: 'Invalid request' }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - }); -}; diff --git a/ts/servertools/tools.sslredirect.ts b/ts/servertools/tools.sslredirect.ts deleted file mode 100644 index 9be6113..0000000 --- a/ts/servertools/tools.sslredirect.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as plugins from '../plugins.js'; -import { Server } from './classes.server.js'; -import { Handler } from './classes.handler.js'; - -export const redirectFrom80To443 = async () => { - const smartexpressInstance = new Server({ - cors: true, - forceSsl: true, - port: 80, - }); - - smartexpressInstance.addRoute( - '/{*splat}', - new Handler('ALL', async (req, res) => { - res.redirect('https://' + req.headers.host + req.url); - }) - ); - - await smartexpressInstance.start(); - - return smartexpressInstance; -}; diff --git a/ts/utilityservers/classes.websiteserver.ts b/ts/utilityservers/classes.websiteserver.ts index a803624..ac7c2f4 100644 --- a/ts/utilityservers/classes.websiteserver.ts +++ b/ts/utilityservers/classes.websiteserver.ts @@ -1,7 +1,6 @@ import * as interfaces from '../../dist_ts_interfaces/index.js'; import { type IServerOptions, TypedServer } from '../classes.typedserver.js'; import * as plugins from '../plugins.js'; -import * as servertools from '../servertools/index.js'; export interface IUtilityWebsiteServerConstructorOptions { addCustomRoutes?: (typedserver: TypedServer) => Promise; @@ -59,10 +58,12 @@ export class UtilityWebsiteServer { appSemVer: this.options.appSemVer || 'x.x.x', }; - // -> /lsw* - anything regarding serviceworker - servertools.serviceworker.addServiceWorkerRoute(this.typedserver, () => { - return lswData; - }); + // -> Service worker version info handler + this.typedserver.typedrouter.addTypedHandler( + new plugins.typedrequest.TypedHandler('serviceworker_versionInfo', async () => { + return lswData; + }) + ); // ads.txt handler this.typedserver.addRoute('/ads.txt', 'GET', async () => {