@fl45h97/eazy
v3.0.1
Published
This is a little framework to enhance security at nodejs endpoints. Furthermore, Logging, Email, HTML ejs file generating, Date formatting and Database connecting is also available
Downloads
363
Maintainers
Readme
Eazy package
Eazy package is supposed to be used easily. It provides some frames to do tasks in server side, for example: api endpoint input validation, Email service, Logsystem, Database connector, Html generator with ejs, custom routing, property validator
Logger
This class is used to create logs to the console, furthermore it can be set up to send email on error.
Example:
Logger.error( 'Error message!', 'Err_code', true); // last parameter is optional. if it set to true, empty lines will appear before and after the message. default=false
Logger.warning("Warning message!",true);
Logger.info('Info message!');
Logger.trace("Trace message!");
Logger.success("Task successfully completed!");
Logging to console can be disabled via
Logger.writeconsole = false;
Sending email message on error can be enabled by
Logger.sendmail = true;
A callback can be attached in case of error. So if you want to implement a different email system or send an http request you can do so.
Logger.errorCallbackFunction = myFunction;
Log level can be set via
Logger.level=Loglevel.trace;
...
export enum LogLevel {
none = 0,
error = 1,
warning = 2,
info = 3,
trace = 4,
}
How to use the api endpoint input validation feature?
First of all, you should create fields which implement the IField
interface
e.g.:
export class DateField implements IField{
public type: FieldType = FieldType.date;
public min: Date;
public max: Date;
public validators: Validator[] = [];
constructor(public name: string, public optional : boolean = false) {
this.min = new Date('1900/01/01');
this.max = new Date('2100/01/01');
this.validators = [DefaultValidators.basicDateFormat];
}
}
- Tip: It is a good practice to let the name property to be changed dinamically, because you might want to use 2 DateField as input in 1 endpoint - Tip: It is a good practice to let the optional property to be changed dinamically, because you might want to reuse the DateField in different models
After that you should define a model which describes an endpoint. eg.:
export class EndpointModel implements IModel {
fromDate : IField = new DateField('from');
toDate : IField = new DateField('to');
groups: IField[][] = [];
public interfaceHelpMessage = () : string => {
return 'Test interface : 2 field is required. to and from Date field.';
}
constructor(){
this.groups= [
[this.fromDate,this.toDate],
]
}
}
As you can see, Models should implements IModel
interface!
interfaceHelpMessage()
should be described correctly, because it is useful when debugging is enabled.
!!! groups
should be assigned!
In the example above, fromDate
and toDate
always have to be present when you request this endpoint! If you want to make fromDate
optional you should set the optional flag:
export class EndpointModel implements IModel {
fromDate : IField = new DateField('from', true);
// ...
}
In the groups you always declare what is a valid request made of!
Now you have the fields and an endpoint model now it's time connect it with a Controller
e.g.:
export class TestController extends Controller {
constructor(){
super();
this.registerRequestValidator({get: new EndpointModel()}, true);
}
async get(req: Request, res: Response): Promise<void> {
throw new Error("Method not implemented.");
}
async post(req: Request, res: Response): Promise<void> {
throw new Error("Method not implemented.");
}
async patch(req: Request, res: Response): Promise<void> {
throw new Error("Method not implemented.");
}
async put(req: Request, res: Response): Promise<void> {
throw new Error("Method not implemented.");
}
async delete(req: Request, res: Response): Promise<void> {
throw new Error("Method not implemented.");
}
}
Your controller should extends Controller
class, in this way you can use your Models in the controller.
this.registerRequestValidator({get: new EndpointModel()}, true);
This function register your Model to the Controller. registerRequestValidator(...)
requires 1 parameter (and 1 optional):
The first parameter should be an object of IValidators
interface which looks like this:
export interface IValidators {
get?: IModel;
patch?: IModel;
post?: IModel;
put?: IModel;
delete?: IModel;
}
The optional paramęter helpMessage
should be used only in development phase. It returns an http site with useful, detailed information, if model validation fails. In this way, you can debug your endpoint easily.
As you can see, you can either use one IModel
with different groups for all method or you can declare different IModel
for different method. It's up to you.
Guard
Another great security feature to use. For example, you can create a Guard
for your protected endpoint, in this way you check permissions, rights, users, whatever you want... Guards have to extend the Guard
baseclass.
e.g.:
export class MyGuard extends Guard{
protected checkGet(req: Request, res: Response): boolean {
res.status(HttpCodes.forbidden).send();
return false;
}
protected checkPost(req: Request, res: Response): boolean {
return true;
}
protected checkPatch(req: Request, res: Response): boolean {
return true;
}
protected checkDelete(req: Request, res: Response): boolean {
return true;
}
protected checkPut(req: Request, res: Response): boolean {
return true;
}
}
You can use the HttpCodes
enum to get the proper http status code.
- Please note: If your check functions return true, then the Guard
baseclass will call then next()
function. If it returns false or does not return, then nothing happens. If your customGuard returns false, then you should send a response also.
Routing
Router handler is also available in order to register Guards and Model validation correctly. You should first create a Routes.ts file. e.g.:
export class Routes {
public static all : IRoute[] =
[
{path: '/test', guard: new MyGuard(), controller: new TestController()}
]
}
guard is an optional paramter.
After your routes are up, you should register them into the express
app. e.g.:
const app : Application = express();
app.use('/api',RoutesHandler.register(Routes.all));
- Tip: You can use the Logger.errorCallbackFunction(...)
which is called at the end of Logger.error(...)
. In this way you can declare what should happen on error. E.g.:
You could send a custom email (via EmailService.sendMail(...)
) generated by HtmlGenerator
You could save error message to a custom file
You can terminate the process
You can make an http call to a custom website
Validator
Validator
(please don't confuse with IValidators
) Is used in IField
interface, in order to create custom validators. E.g.: phone,mail,only letter and so on.
You can create a Validator
like
new Validator(/^[A-Za-z]+$/, 'It must contains letters only.');
In the DefaultValidators
class there are some validators provided already. I will add more in future.
export class DefaultValidators {
public static readonly containsNumber: Validator = new Validator(/\d+/, 'It must contains a digit.');
public static readonly containsAlphabet: Validator = new Validator(/[a-zA-Z]/, 'It must contains an english letter.');
public static readonly onlyLetters: Validator = new Validator(/^[A-Za-z]+$/, 'It must contains letters only.');
public static readonly onlyNumbers: Validator = new Validator(/^[0-9]+$/, 'It must contains numbers only.');
public static readonly onlyMixOfAlphaNumeric: Validator = new Validator(
/^([a-zA-Z0-9])+$/,
'It must be a mixture of english letters and digits!',
);
public static readonly basicDateFormat: Validator = new Validator(
/^([0-9]){4}([-/]){1}[0-9]{2}([-/])[0-9]{2}/,
'It must be a date formatted like YYYY-MM-DD or YYYY/MM/DD',
);
}