Skip to content

Commit

Permalink
refactor: remove render and static method with other files
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mdy-m committed Aug 22, 2024
1 parent 843ac9d commit a102f38
Show file tree
Hide file tree
Showing 8 changed files with 13 additions and 230 deletions.
27 changes: 3 additions & 24 deletions examples/CRUD/app.ts
Original file line number Diff line number Diff line change
@@ -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<void>) => {
// 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,
});
9 changes: 0 additions & 9 deletions examples/CRUD/views/index.ejs

This file was deleted.

67 changes: 8 additions & 59 deletions lib/core/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [];
Expand Down Expand Up @@ -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<void>) => {
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
}
};
}
}
25 changes: 1 addition & 24 deletions lib/core/router/response.ts
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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);
};
};
63 changes: 1 addition & 62 deletions lib/core/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Gland.RouteHandler>(handlers);

Expand Down
46 changes: 0 additions & 46 deletions lib/helper/View.ts

This file was deleted.

5 changes: 0 additions & 5 deletions lib/types/gland.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion lib/types/http.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit a102f38

Please sign in to comment.