0.13.4 • Published 10 days ago

interlayer v0.13.4

Weekly downloads
5
License
Apache-2.0
Repository
github
Last release
10 days ago

interlayer

npm version npm downloads github license Build Status Code Climate

At this point, the server version is still in alpha. You can suggest an idea or report an error here: New issue

The stable version of the server will be implemented after writing all the necessary features and tests to them.

Changelog

CHANGELOG.md

Features

  • Serving Static Content
  • Upload files
  • Auto-reload server on file change (reload on new files not supported)
  • Clusterization
  • Postgres\mysql\redis built-in DAL's for data storage
  • Mailgun\sparkpost\smtp build-in mail sender packages
  • Localization
  • WebScoket

Install

npm install --save interlayer

or

yarn add interlayer

Project tree example

  • /node_modules/
  • package.json
  • /files/ - this folder will be served by config.serve
    • index.html
    • style.css
    • script.js
    • /images/
      • logo.jpeg
  • index.js
  • /i18n/
    • en-US.json
  • /middleware/
    • auth.js
  • /modules/
    • weather.js
  • config.json

Request processing steps

  1. request preparation - filling with functions and objects, see here
  2. search for a module. if not found - search for a file in config.serve and give it to the client
  3. if the module is found, parse the data if the request method is POST
  4. for all matching triggers run middlewares, more see here
  5. if the found module has a prerun in its meta, run it, more see here
  6. finally launch our module(that can use any data from here), in response we wait for see here
  7. convert to json text if meta.toJson || meta.contentType == 'json' || headers'Content-Type' == 'application/json'
  8. done

/modules/weather.js example

exports._obtain = {
    checkIp: true,
    prerun(request, moduleMeta, cb){

    }
};
exports.obtain = (request, cb){
    cb(null, 'good weather');
};

/middleware/auth.js example

exports.triggers = {
    'meta.checkIp': (request, moduleMeta, cb) => {
        if(request.ip === '127.0.0.1'){
            return cb('bad ip');
        }
        cb();
    },
    'request.params.city': (request, moduleMeta, cb) => {
        if(request.params.city === 'london'){
            return cb('bad weather');
        }
        cb();
    }
}

Variants to start server

const config = {
    port: 80,
    serve: ['files']
};
require('interlayer')(config);

or

require('interlayer')('config.json');

or

const server = require('interlayer').server();
server
    .setRootPath(__dirname)
    .loadConfigFile('config.json')
    .start()

config object or config.json file configuration

Avaliable params:

Avaliable properties in config object or config.json file

PropertyDefaultTypeDescription
port8080NumberWeb server port number
secure---ObjectSSL configuration object with paths to files: {key:'',cert:''}
initPath./StringWeb server root path
logPath./StringPath to create the logs.log file
timeout60(sec)NumberTimeout in seconds, then user will see {error: 'TIMEOUT'} Note, execution of the method is not interrupted
workers1NumberNumber of instances for load balancing. If number more than 1, uses node.js cluster
restartOnChangefalseBooleanFlag determine is server will restart automatically when files in the folder with modules was changed
useDals---ObjectdalName = dalConfigThe configuration object for dal modules to be used. Supports redis(redis:{}), mysql(mysql:{} and default will be {host: '127.0.0.1',user: 'root'}), postgress(postgress:{} and default will be {host: '127.0.0.1',user: 'root'}). For config built-in redis see here🌍, mysql see here🌍, postgres see here🌍 (Example of dal's config: useDals: {mysql: {port: 6375, user: 'admin'}, redis: {}})
useEmailSenders---ObjectemailSenderName = emailSenderConfigThe configuration object for the mail senders to be used. Supports mailgun(mailgun:{}), sparkpost(sparkpost:{}), smtp(smtp:{}). For config built-in mailgun see here🌍, sparkpost see here🌍, smtp see here🌍
serve---Array[Strings[]]An array folders to serve. Priority over the last folder
modules./modulesArray[Strings[]]An array folders with modules. Priority over the last folder. (Default directory is './modules' unless otherwise specified.) How to create
views./filesArray[Strings[]]An array of folders with files, which you can be uses as templates, or returned through the api(by using request.getView). Priority over the last folder. (Default directory is './files' unless otherwise specified.)
i18n./i18nArray[Strings[]]An array of folders with localization files. Priority over the last folder. (Default directory is './i18n' unless otherwise specified.) How to create
dals---Array[Strings[]]An array of folders with your dal(Data Access Layer) modules. Priority over the last folder. How to create
emailSenders---Array[Strings[]]An array of folders with your email senders. Priority over the last folder. How to create
middleware---Array[Strings[]]An array of folders with middlewares. Priority over the last folder. How to create
middlewareOrder---Array[Strings[]]An array with ordered names of middlewares
middlewareTimeout10(sec)NumberTimeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted
skipDbWarningfalseBooleanSkip warning in console if useDals not defined in config
defaultHeaders---ObjectheaderName = headerValueAn object with default headers, which have to be added to the every response
debugfalseBooleanAllow to display log.d in console and add to the logs.log file
instantShutdownDelay1500(ms)NumberDelay in milliseconds after server will shutdown on process SIGINT or SIGTERM signal, or process message: shutdown
retryAter10(sec)NumberTime in seconds for Retry-After response header with server HTTP 503 status. Works until instantShutdownDelay
noDelaytrueBooleanFlag to enable/disable Nagle algoritm for all connections. See here🌍
websocket---Boolean/ObjectStart websocket. If true then on the same port as server, except as stated in the Object. See here🌍. Initialized server instance can be found in initFunction simpleRequest.websocket
useHttpErrorFilesfalseBooleanPossible errors will be given as files if they are found in directories specified in addViewPath
skipParsePostfalseBooleanSkip parse POST
formidableOptions{}ObjectSet formidable options on parse data when headers'content-type' not null, list of options see here🌍
startInitstrueBooleanStart functions that were added via Module app.setInit
disableLogFilefalsebooleanDisable to write log file, the console.log and others continues to be written by the console

Intrlayer instance configure

let serverInstance = require('interlayer').server();

How to use

let server = require('interlayer').server();
server.setRootPath(__dirname);
server.loadConfigFile('config.json');
server.start();

Avaliable methods:

PropertyDefaultTypeDescription
start(configObject / null)---ObjectStarting the server with/without the configuration object
loadConfigFile(path)---StringInitializing configuration from file
setConfig(configObject)---ObjectSetting a configuration from an object
getConfig(configObject)---ObjectGet the resulting configuration
setRootPath(path)./StringSet root directory
setLogPath(path)./StringSet a directory of the log file
setPort(port)8080NumberSet the server port
setSecure(secureObject)---ObjectSSL configuration object with paths to files: {key:'',cert:''}
setWorkersCount(workerNumber)1NumberNumber of instances for load balancing. If number more than 1, uses node.js cluster
setTimeout(timeout)60(sec)NumberTimeout in seconds, then user will see {error: 'TIMEOUT'} Note, execution of the method is not interrupted
setDefaultHeaders(headersObject)---ObjectAn object with default headers, which have to be added to the every response
setRestartOnChange([true / false])falseBooleanBoolean value determine is server will restart automatically when files in the folder with modules was changed
setSkipDbWarning([true / false])falseBooleanSkip warning in console if useDals not defined in config
setDebugMode([true / false])falseBooleanAllow to display log.d in console
setNoDelay([true / false])trueBooleanFlag to disable/enable Nagle algoritm for all connections. See here🌍
setInstantShutdownDelay(timeout)1500(ms)NumberDelay in milliseconds after server will shutdown on process SIGINT or SIGTERM signal, or process message: shutdown
setRetryAter(timeout)10(sec)NumberTime in seconds for Retry-After response header with server HTTP 503 status. Works until config.instantShutdownDelay
addEmailSender(emailSenderName, emailSenderConfig)---String, ObjectAdd an email sender. Priority over the last folder. How to create
addDalPath(path, [path, [path]])---StringAdd path to DAL's(Data Access Layer) modules. Priority over the last added path
addDal(dalName, dalConfig)---String, ObjectThe configuration(dalConfig) for dal module(dalName) to be used. Out of the box is available redis(for use specify redis, {}), mysql(for use specify mysql, {} and default dalConfig will be {host: '127.0.0.1',user: 'root'}), postgress(for use specify postgress, {} and default dalConfig will be {host: '127.0.0.1',user: 'root'}). For configure redis see here🌍, mysql see here🌍, postgres see here🌍
addMiddlewarePath(path, [path, [path]])---String, ...Add path to middleware modules. Priority over the last added path. How to create
setMiddlewareOrder(middlwareName, middlwareName)---String or ArrayStringsAn array(or arguments) with ordered names of middlewares
setMiddlewareTimeout(timeout)10(sec)NumberTimeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted
addModulesPath(path, [path, [path]])./modulesString, ...Add path to modules. Priority over the last added path. (Default directory is './modules' unless otherwise specified.) How to create
addI18nPath(path, [path, [path]])./i18nString, ...Add path to localization files. Priority over the last added path. (Default directory is './i18n' unless otherwise specified.) How to create
addServePath(path, [path, [path]])---String, ...Add path to Serving Static Content. Priority over the last added path
addViewPath(path, [path, [path]])./filesString, ...Folders with files, which you can be uses as templates, or returned through the api(by using request.getView). Priority over the last folder. (Default directory is './files' unless otherwise specified.)
setWebsocketConfig(websocket)---Boolean/ObjectStart websocket. If true then on the same port as server, except as stated in the Object. See here🌍. Initialized server instance can be found in initFunction simpleRequest.websocket
setUseFilesAsHTTPErrors([true / false])falseBooleanPossible errors will be given as files if they are found in directories specified in addViewPath
setSkipParsePost([true / false])falseBooleanSet skip parse POST
setFormidableOptions({}){}ObjectSet formidable options on parse data when headers'content-type' not null, list of options see here🌍
disableInits([true / false])trueStart functions that were added via Module app.setInit
disableLogFile([true / false])falsebooleanDisable to write log file, the console.log and others continues to be written by the console

Module creation

Example of modules/myModule.js

const app = require('interlayer').module();
exports.module = app;

let log = app.getLog('myModuleId');
app.setMeta({asJson: true});
app.setInit((request, requestCallback)=>{
    log.i('Module inited');
    requestCallback();
});

app.addMethod('myMethod', {toJson: true}, (request, requestCallback)=>{
    let fullLog = request.modifyLog(log);
    log.i('I am log without requestId but with myModuleId');
    request.log.i('I am log with requestId but without myModuleId');
    fullLog.i('I am log with requestId and with myModuleId');
    requestCallback(null, {ok: true}, 200, {}, false);
});//Could be called in the path of /myModule/myMethod

Avaliable app methods

const app = require('interlayer').module();
MethodProperty typesDescription
getLog(name)StringGet the object to output messages to the console. Object of {i:funcion, e:function, d: function, w: function, c: function} type
setMeta(metaObject)ObjectSet the default parameters for all methods of this module. metaObject
setInit(initFunction)FunctionSet the function to initialize the module at server start. initFunction
addMethod(methodUrl, [methodMeta,] methodFunction)String, Object, FunctionAdds a new method with/without info(meta). methodMeta and methodFunction
add(methodUrl, [methodMeta,] methodFunction)String, Object, FunctionAlias for addMethod
setMethodInfo(methodUrl, methodMeta)String, ObjectSets info(meta) for method. methodMeta
info(methodUrl, methodMeta)String, ObjectAlias for setMethodInfo
getMethod(methodUrl)StringReturns the method function
getMethodInfo(methodUrl, [withGlobalMeta])String, BooleanReturns method info(meta)

initFunction(simpleRequest)

  • simpleRequest.url - Empty string
  • simpleRequest.headers - Empty object
  • simpleRequest.DAL - DAL objects if initialised
  • simpleRequest.config - Configuration object
  • simpleRequest.websocket - websocket server instanse if initialised

... and functions as in methodFunction request except getResponse, getRequest and other http request methods See here🌍

metaObject and methodMeta default paramerets

KeyTypeDescription
default = methodFunctionFunctionModule(not method) function, can be used to output HTML, Available at /moduleUrl. Only for metaObject. methodFunction
html = methodFunctionFunctionSame as default. Only for metaObject
find = methodFunctionFunctionThe method search function is only triggered if no other methods have been processed. Only for metaObject. See methodFunction
pathStringChanges methodUrl to path
addToRootBooleanSkip moduleUrl and use methodUrl or path as url to method
aliasStringAlias path to method
timeoutNumberSeconds until HTTP 408(Request timeout)
noDelayBooleanDisable/enable the use of Nagle's algorithm. See here🌍
middlewareTimeoutNumberTimeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted
prerun = prerunFunctionFunctionFunction or link to function which will be runned before method. May be usefull for preparing request. prerunFunction
toJsonBooleanConvert response to JSON string
contentTypeStringSame as toJson if contentType==json
skipRequestLogStringSkip request log output
hiddenBooleanSkip method from return information while calling request.getMethodsInfo
skipParsePostBooleanSkip parse POST

prerunFunction(request, moduleMeta, requestCallback)

methodFunction(request, requestCallback):

request: | Methods | Property types | Description | | --- | --- | --- | | modifyLog(log) | Object | Add to log object requestId for log created with global.logger | | getView(file, callback) | String, Function | Return file in callback from paths specified in config.views or in server.setViewPath(). callback = (error, data) | | getViewSync(file) | String | Synchronous request.getView | | getFile(file, callback) | String, Function | Return file as is. callback = (error, data, {'Content-type':''}) | | addCookies(key, value) | String, String | Set coockie for response | | rmCookies(key) | String | Remove coockie from responce | | i18n(key[, defaultValue]) | String, String | Return translate for key or return defaultValue| | obtainI18n() | --- | Return object with languages | | getMethodsInfo(showHidden) | Boolean | Returns all methods (except hidden methods if showHidden is not specified) | | lockShutdown() | --- | Blocks the termination of the process until the request is completed | | unlockShutdown() | --- | Unlock the termination of the process | | getResponse() | --- | Returns the original responce | | getRequest() | --- | Returns the original request | | error(text) | String | Returns 503 http code | | end(text[, code[, headers[, type]]]) | String[, Number[, Object, String]] | Returns code http code with text(as binary if type==bin) and headers|

request: | Property | Type | Description | | --- | --- | --- | | config | Object | An object of configuration specified at start of server | | ip | String | Client ip adress | | url | String | Request url | | path | String | Request path(module/method) | | method | String | Uppercased type of request - POST|GET|... | | isPost | Boolean | true|false | | params | Object | An object of parsed GET params | | post | Object | An object of parsed POST params(with formidable🌍) | | files | Object | An object of uploaded files(with formidable🌍) | | cookies | Object | An object of parsed cookies | | headers | Object | An object of request headers | | DAL | Object | An object with DALs, which was initialized by config.useDals or server.addDal() | | mail | Object | An object with mail senders, which was initialized by config.useEmails or server.addEmailSender() | | id | String | requestId | | log | Object | The same as global.logger.create(moduleID), but with requestID included(not include moduleID) | | helpers | Object | requiest.helpers |

request.helpers

MethodsProperty typesDescription
helpers.generateId()---Geneate 8-character identifier(a-zA-Z0-9)
helpers.toJson(obj)*Convert obj to JSON string
helpers.clearObj(obj, toRemove)Object, ArrayDelete parameters of obj from toRemove array of strings
helpers.isBoolean(val)*Check is val string is Boolean(truefalse)
helpers.JSV(json, schema, envId)Object, Object, StringSee here🌍. Create environment with envId and call validate with json and schema
helpers.mime()Objectreturn mime type by file extension or fallback or 'application/octet-stream'

requestCallback(error, data, httpCode, responseHeaders, isBinary)

  • error - null or undefined or String or Object
  • data - null or String or Object or Binary(if isBinary = true)
  • httpCode - null or Number See here🌍
  • responseHeaders - null or Object See here🌍 If Content-Type = application/json then data will be returned as JSON
  • type - null or 'bin'. If 'bin' then data will be returned as Buffer

Global objects added

global.logger

Object to create log with global.logger.create(logName) or global.logger(logName) were logName is String. log avaliable methods: | Methods | Description | | --- | --- | | i | Parameters same as for console.log. See here🌍 | | w | Parameters same as for console.warn. See here🌍 | | e | Parameters same as for console.error. See here🌍 | | d | Parameters same as for console.debug. See here🌍 | | c | Parameters same as for console.log. See here | Note that this type of logging don't allow to track the request id.

To have ability track the request id use the request.modifyLog method:

let log = global.logger.create('moduleID');
exports.myMethod = (request, cb)=>{
    let log = request.modifyLog(log);
}

global.intervals

Object with methods: | Methods | Property types | Description | | --- | --- | --- | | add(function, timeout) | Function, Number | Return key | | add(function, timeout = {year: '*', month: '*', date: '*', day: '*', hour: '*', minute: '*', second: '*'}) | Function, Object | Any of the parameters can be "*" or "2020,2040" - the options are listed in commas. Return key | | del(key) | String | Remove by key | | disable(key, flag) | String, Boolean | Disable/Enable interval by key and flag | | enable(key) | String | Enable interval by key | The startup interval is every second. If the start conditions (timeout) match, function is called with a parameter as a function to delete the interval - similar call to global.intervals.del(key).

Often used inside the function initFunction

Remember, if at server startup config.startInits = false or disableInits(false) then functions added via setInit(initFunction) will not be added to global.intervals


Features

Logging:

const log = global.logger('moduleID');
log.i(); // Usual log - displayed in green
log.w(); // Warn - displayed in yellow
log.e(); // Error - displayed in red
log.c(); // Critical error - displayed in white

Note that this type of logging don't allow to track the request id. To have ability track the request id use the request.modifyLog method:

const log = global.logger('moduleID');
exports.myMethod = (request, cb)=>{
    let log = request.modifyLog(log);
}

Or use the request.log instead, if 'moduleID'(identifier specified in global.logger.create function) not required.

let log = global.logger.create('moduleID');
exports.myMethod = (request, cb)=>{
    let log = request.log;
}

---

Use dals:

request.DAL.redis.get('somekey', (err, data) => {
    if(err){
        request.log.e('redis.get somekey', err);
        return cb(err);
    }
    ...
});
request.DAL.mysql.query('SELECT * FROM users WHERE login = ?', ['admin'], (err, data, fields) => {
    if(err){
        request.log.e('mysql.query', err);
        return cb(err);
    }
})

Use email senders

request.mail.mailgun.send({}, callback) -> see params here https://documentation.mailgun.com/api-sending.html#sending
request.mail.sparkpost.send({}, callback) -> see params here https://developers.sparkpost.com/api/transmissions.html#header-transmission-attributes
//or use initialized senders as you want
request.mail.mailgun.client -> https://www.npmjs.com/package/mailgun-js
request.mail.sparkpost.client -> https://www.npmjs.com/package/sparkpost

Create dal

Example of dals/nameofdal.js Then you can add nameofdal to config.useDals array (ex: config.useDals = {nameofdal: {...config}};)

// init is not required
exports.init = (config, dalConfig) => {

};

// but methods is required
exports.methods = {
    get: () => {},
    set: () => {}
}

Create email sender

Example of emailSenders/nameofsender.js Then you can add nameofsender to config.useEmailSenders array (ex: config.useEmailSenders = {nameofsender: {...config}};)

// init is required
exports.init = (config, emailConfig) => {

};

// send is required
exports.send = (email, cb)=>{

}

Create middleware

Example of middleware/session.js

exports.triggers = {
    'meta.checkSessions': 'checkSession', // you can specified string - name of function exported in module
    'request.params.test': exports.test // also you can specified function or method of object
};

// but methods is required
// request context and callback described in module
exports.checkSession = (request, moduleMeta, cb) => {

};

exports.test = (request, moduleMeta, cb) => {

};

or

exports.run = (request, moduleMeta, cb)=>{

};
// this creates
// exports.triggers = {
//     '*': exports.run
// };

Localization

Example of i18n/en.js

Note! You have to use double quotes, instead single quotes, because it's json file These are the actual keys used for error output.

{
    "Not found": "Nothing found, 404, Bill Gates site",
    "<center>Error 404<br>Not found</center>": "<h1>404 HTTP error.<h1>Not found</center>",
    "Service Unavailable. Try again another time.": "503 HTTP error."
}
0.13.4

10 days ago

0.13.0

8 months ago

0.13.1

8 months ago

0.13.2

8 months ago

0.13.3

8 months ago

0.12.3

8 months ago

0.12.4

8 months ago

0.12.5

8 months ago

0.12.0

12 months ago

0.12.1

12 months ago

0.12.2

12 months ago

0.11.1

3 years ago

0.11.0

4 years ago

0.10.11

4 years ago

0.10.10

4 years ago

0.10.9

4 years ago

0.10.3

4 years ago

0.10.4

4 years ago

0.10.5

4 years ago

0.10.6

4 years ago

0.10.7

4 years ago

0.10.8

4 years ago

0.10.0

4 years ago

0.9.0

4 years ago

0.9.1

4 years ago

0.8.4

4 years ago

0.7.2

4 years ago

0.7.1

4 years ago

0.7.3

4 years ago

0.8.1

4 years ago

0.8.0

4 years ago

0.8.3

4 years ago

0.8.2

4 years ago

0.7.0

4 years ago

0.6.1

4 years ago

0.6.0

5 years ago

0.5.2

5 years ago

0.5.1

5 years ago

0.5.0

5 years ago

0.4.15

5 years ago

0.4.14

5 years ago

0.4.13

5 years ago

0.4.12

5 years ago

0.4.10

5 years ago

0.4.9

5 years ago

0.4.8

5 years ago

0.4.7

5 years ago

0.4.6

5 years ago

0.4.5

6 years ago

0.4.4

6 years ago

0.4.3

6 years ago

0.4.2

6 years ago

0.4.1

6 years ago

0.4.0

6 years ago

0.3.20

6 years ago

0.3.19

6 years ago

0.3.18

7 years ago

0.3.17

7 years ago

0.3.16

7 years ago

0.3.15

7 years ago

0.3.14

7 years ago

0.3.13

7 years ago

0.3.12

7 years ago

0.3.11

7 years ago

0.3.10

7 years ago

0.3.9

7 years ago

0.3.8

7 years ago

0.3.7

7 years ago

0.3.6

7 years ago

0.3.5

7 years ago

0.3.4

7 years ago

0.3.3

7 years ago

0.3.2

7 years ago

0.3.1

7 years ago

0.2.17

7 years ago

0.3.0

7 years ago

0.2.16

7 years ago

0.2.15

7 years ago

0.2.14

7 years ago

0.2.13

7 years ago

0.2.12

7 years ago

0.2.11

8 years ago

0.2.10

8 years ago

0.2.9

8 years ago

0.2.8

8 years ago

0.2.7

8 years ago

0.2.6

8 years ago

0.2.5

8 years ago

0.2.4

8 years ago

0.2.3

8 years ago

0.2.2

8 years ago

0.2.1

8 years ago

0.2.0

8 years ago

0.1.9

8 years ago

0.1.8

8 years ago

0.1.7

8 years ago

0.1.6

8 years ago

0.1.5

8 years ago

0.1.4

8 years ago

0.1.3

8 years ago

0.1.2

8 years ago

0.1.1

8 years ago

0.1.0

8 years ago

0.0.9

8 years ago

0.0.8

8 years ago

0.0.7

8 years ago

0.0.6

8 years ago

0.0.5

8 years ago

0.0.4

8 years ago

0.0.3

8 years ago

0.0.2

8 years ago

0.0.1

8 years ago