From a102f3885e1027f2a25423d8b7825a03191f4215 Mon Sep 17 00:00:00 2001 From: mahdi Date: Thu, 22 Aug 2024 20:34:57 +0330 Subject: [PATCH] refactor: remove render and static method with other files --- examples/CRUD/app.ts | 27 ++------------ examples/CRUD/views/index.ejs | 9 ----- lib/core/middleware/index.ts | 67 +++++------------------------------ lib/core/router/response.ts | 25 +------------ lib/core/server.ts | 63 +------------------------------- lib/helper/View.ts | 46 ------------------------ lib/types/gland.d.ts | 5 --- lib/types/http.d.ts | 1 - 8 files changed, 13 insertions(+), 230 deletions(-) delete mode 100644 examples/CRUD/views/index.ejs delete mode 100644 lib/helper/View.ts diff --git a/examples/CRUD/app.ts b/examples/CRUD/app.ts index 8f7906a..2502627 100644 --- a/examples/CRUD/app.ts +++ b/examples/CRUD/app.ts @@ -1,36 +1,15 @@ import { WebServer } from '../../lib/core/server'; import { Context } from '../../lib/types/types'; -import ejs from 'ejs'; const app = new WebServer(); -// Configure the template engine -app.engine('.ejs', async (path: string, options: object, callback: (err: Error | null, rendered?: string) => void) => { - ejs.renderFile(path, options, callback); -}); - -// Set default view engine and views directory -app.set('view engine', '.ejs'); -app.set('views', './views'); - -app.use(async (ctx: Context, nxt: Function) => { - console.log('hello from mid'); - await nxt(); -}); -// // Define routes -app.use('/api', async (ctx: any, next: () => Promise) => { - // Example middleware for API routes - console.log('API route accessed'); - await next(); -}); - -// Define a route with `all` method +// Define a dynamic route with `all` method app.all('/about', async (ctx: Context) => { - ctx.render('index'); + ctx.end('REQUEST ' + ctx.method + ctx.url); }); // Start the server app.init({ port: 3000, - host: 'localhost', + host: '127.0.0.1', logger: true, }); diff --git a/examples/CRUD/views/index.ejs b/examples/CRUD/views/index.ejs deleted file mode 100644 index fc4b31f..0000000 --- a/examples/CRUD/views/index.ejs +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - Document - - hello brother - diff --git a/lib/core/middleware/index.ts b/lib/core/middleware/index.ts index c6028f9..f44bbf8 100644 --- a/lib/core/middleware/index.ts +++ b/lib/core/middleware/index.ts @@ -2,8 +2,11 @@ import { ServerUtils } from '../../helper'; import { Context, MidsFn } from '../../types/types'; import { Gland } from '../../types/gland'; import { Router } from '../router'; -import { join, resolve } from 'path'; +import path, { extname, join, posix, resolve } from 'path'; import { createReadStream, existsSync, statSync } from 'fs'; +import { IncomingMessage } from 'http'; +import { ServerResponse } from 'http'; +import { parse as parseUrl } from 'url'; export namespace Gmid { let mids: MidsFn[] = []; @@ -51,64 +54,10 @@ export namespace midManager { } }); } else { - // If the first argument is not a string, treat it as a middleware - const middlewares = [path, ...handlers].flat() as Gland.Middleware[]; - middlewares.push(...middlewares); + // Handle when path is not a string + const allMiddlewares = [path, ...handlers].flat(); + const uniqueMiddlewares = Array.from(new Set(allMiddlewares)); + middlewares.push(...uniqueMiddlewares); } } } - -export namespace Static { - export function serve(root: string): Gland.Middleware { - const absoluteRoot = resolve(root); - console.log('absoluteRoot:', absoluteRoot); - - return async (ctx: Context, next: () => Promise) => { - try { - // Ensure the URL is correctly parsed and cleaned - const requestedPath = ctx.url! || '/'; - console.log('requestedPath:', requestedPath); - - const filePath = resolve(join(absoluteRoot, requestedPath)); - console.log('filePath:', filePath); - - // Prevent path traversal attacks - if (!filePath.startsWith(absoluteRoot)) { - console.log('Path traversal attempt detected'); - await next(); - return; - } - - // Check if the file exists and is a file - if (existsSync(filePath) && statSync(filePath).isFile()) { - const stat = statSync(filePath); - console.log('File found:', filePath); - - // Set the appropriate headers - ctx.rs.setHeader('Content-Type', ServerUtils.getContentType(filePath)); - ctx.rs.setHeader('Content-Length', stat.size); - ctx.rs.setHeader('Last-Modified', stat.mtime.toUTCString()); - - // Handle ETag and If-None-Match for caching - const etag = ServerUtils.generateETag(stat); - ctx.rs.setHeader('ETag', etag); - if (ctx.rq.headers['if-none-match'] === etag) { - ctx.rs.writeHead(304); - ctx.rs.end(); - return; - } - - // Stream the file to the response - const stream = createReadStream(filePath); - stream.pipe(ctx.rs); - } else { - console.log('File not found, proceeding to next middleware'); - await next(); // File not found, proceed to next middleware - } - } catch (error) { - console.error('Error in static middleware:', error); - await next(); // On error, proceed to next middleware - } - }; - } -} diff --git a/lib/core/router/response.ts b/lib/core/router/response.ts index 6919384..da22679 100644 --- a/lib/core/router/response.ts +++ b/lib/core/router/response.ts @@ -1,6 +1,4 @@ import { ServerResponse, CookieOptions } from 'http'; -import { WebServer } from '../server'; -import { resolve } from 'path'; const PROTO = ServerResponse.prototype; PROTO.clearCookie = function (name: string): ServerResponse { if (!name) { @@ -60,25 +58,4 @@ PROTO.redirect = function (url: string, statusCode: number = 302): void { this.writeHead(statusCode, { Location: url }); this.end(); -}; - -PROTO.render = async function (view: string, options: object = {}, callback?: (err: Error | null, html?: string) => void) { - const app: WebServer | undefined = (this as any).server; - if (!app) { - throw new Error('Server instance is not available on the response object.'); - } - const done = - callback || - ((err: Error | null, str?: string) => { - if (err) { - throw new Error(`${err}`); - } - this.end(str); - }); - - // Merge res.locals - options = { ...app.locals, ...options }; - - // Render the view - app.render(view, options, done); -}; +}; \ No newline at end of file diff --git a/lib/core/server.ts b/lib/core/server.ts index 478fd5d..cd994c9 100644 --- a/lib/core/server.ts +++ b/lib/core/server.ts @@ -7,77 +7,16 @@ import { Router } from './router'; import { LoadModules } from '../helper/load'; import { Context } from '../types/types'; import { METHODS } from 'http'; -import { midManager, Static } from './middleware'; -import { View } from '../helper/View'; -import path from 'path'; +import { midManager } from './middleware'; export class WebServer extends Server implements Gland.Listener, Gland.APP { private middlewares: Gland.Middleware[] = []; - private engines: { [ext: string]: Gland.Engine } = {}; - private settings: { [key: string]: any } = {}; - private cache: { [name: string]: any } = {}; - public locals: { [key: string]: any } = {}; - constructor() { super(); } - // Static method to serve static files - static(root: string, options?: Static.Options): this { - this.use(Static.serve(root, options)); - return this; - } use(path: string | Gland.Middleware, ...handlers: (Gland.Middleware | Gland.Middleware[])[]): this { midManager.process(path, handlers, this.middlewares); return this; } - engine(ext: string, callback: Gland.Engine): this { - ext = ext.startsWith('.') ? ext : `.${ext}`; - this.engines[ext] = callback; - return this; - } - - set(name: string, value?: any): this { - this.settings[name] = value; - return this; - } - - get(name: string): any { - return this.settings[name]; - } - // Render a view - render(name: string, options: object, callback: (err: Error | null, rendered?: string) => void) { - const done = - callback || - function (err: Error | null, str?: string) { - if (err) { - throw err; - } - }; - - let view = this.cache[name]; - const engines = this.engines; - const renderOptions = { ...this.locals, ...options }; - if (!view) { - const ViewConstructor = View; - view = new ViewConstructor(name, { - defaultEngine: this.get('view engine'), - root: this.get('views'), - engines: engines, - }); - if (!view.path) { - const dirs = Array.isArray(view.root) && view.root.length > 1 ? `directories "${view.root.slice(0, -1).join('", "')}" or "${view.root[view.root.length - 1]}"` : `directory "${view.root}"`; - const err: any = new Error(`Failed to lookup view "${name}" in views ${dirs}`); - err.view = view; - return done(err); - } - // Cache the view - if (renderOptions.cache) { - this.cache[name] = view; - } - } - - // Render the view - tryRender(view, renderOptions, done); - } all(path: string, ...handlers: Gland.RouteHandler[]): this { const uniqueHandlers = new Set(handlers); diff --git a/lib/helper/View.ts b/lib/helper/View.ts deleted file mode 100644 index e08f99c..0000000 --- a/lib/helper/View.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { resolve, join, extname } from 'path'; -import { existsSync } from 'fs'; - -export class View { - name: string; - root: string | string[]; - defaultEngine: string; - ext: string; - path: string | null = null; - engines: { [ext: string]: (path: string, options: object, callback: (err: Error | null, rendered?: string) => void) => void }; - - constructor(name: string, options: { root: string | string[]; defaultEngine: string; engines: { [ext: string]: any } }) { - this.name = name; - this.root = options.root; - this.defaultEngine = options.defaultEngine; - this.engines = options.engines; - - // Determine file extension - this.ext = extname(name) || this.defaultEngine; - - // Find the file path - this.path = this.lookup(name); - } - - lookup(name: string): string | null { - const roots = Array.isArray(this.root) ? this.root : [this.root]; - - for (const root of roots) { - const fullPath = resolve(join(root, name + this.ext)); - if (existsSync(fullPath)) { - return fullPath; - } - } - return null; - } - - render(options: object, callback: (err: Error | null, rendered?: string) => void) { - const engine = this.engines[this.ext]; - - if (!engine) { - return callback(new Error(`No view engine found for extension "${this.ext}"`)); - } - - engine(this.path!, options, callback); // Path is guaranteed to be set by lookup - } -} diff --git a/lib/types/gland.d.ts b/lib/types/gland.d.ts index 439c43b..93a29d6 100644 --- a/lib/types/gland.d.ts +++ b/lib/types/gland.d.ts @@ -20,13 +20,8 @@ export namespace Gland { setHeaders?: (res: ServerResponse, path: string, stat: any) => void; } export interface APP { - engine(ext: string, callback: Engine): this; all(path: string, ...handlers: RouteHandler[]): this; use(path: string | Middleware, ...handlers: Middleware[]): this; - engine(ext: string, callback: Function): this; - set(name: string, value?: any): this; - get(name: string): any; - static(root: string); } export interface Engine { (path: string, options: object, callback: (err: Error | null, rendered?: string) => void): void; diff --git a/lib/types/http.d.ts b/lib/types/http.d.ts index fdc6525..40ab82b 100644 --- a/lib/types/http.d.ts +++ b/lib/types/http.d.ts @@ -9,7 +9,6 @@ declare module 'http' { clearCookie(name: string): this; cookie(name: string, value: string, options?: CookieOptions): this; redirect(url: string, statusCode?: number): void; - render(view: string, options?: object, callback?: (err: Error | null, html?: string) => void): void; } interface CookieOptions { expires?: Date;