lapland-js-spec

Lapland specification (Vanilla* JS backend framework)

*the only exception is mongodb module

Lapland is YAFJSF (yet another fraken JavaScript framework) I’m curious to play in development of.))

Here’s the repo with the main module itself.

I struggled for some time trying to find the names for my modules that I would actually like. Until I decided to see them as actors working on the common task - in a sense like Santa’s elfs. So the whole naming theme goes from there.

Santa's Workshop sketch by Cécile Carre

kind of inspired by Santa’s Workshop artwork by Cécile Carre

Modular structure

Flowcharts

Interaction Diagrams are on this page

Lapland

(structure)./lapland.js (all files in repo)

The main module to load and start the rest of the framework. It exports the lapland object with the method to start it:

lapland.wish(details)

an async method that takes details object with the next properties:

Santa

(structure)./santa/index.js (all files in repo)

A server module, creates an http-server and exports it, includes the request handler function shop.

shop

(structure)./santa/index.js (all files in repo)

A shop (as in “Santa’s workshop where elves are making and wrapping gifts for the children”) is the function where the handling of every client’s request to the Santa (server) happens.

home

(structure)./home/*.html, *.css, *.js, *.jpg, *.png, … (all files in repo)

Public folder with the front-end files (client side) available on the server for client requests.

apiBox

(structure)./api/routes/*.js (all files in repo)

An /api folder with js files exporting certain api handling functions.

apiElf

(structure)./apielf/index.js (all files in repo)

A module that loads api processing functions from apiBox into an api object, selects and calls the appropriate one when request requires it.

sesElf

(structure)./seself/index.js (all files in repo)

A module that handles sessions when it is necessary and allows or denies the execution of the api handler function. It provides methods to start or end sessions, list them, label, limit by time.

It exports a sesElf object with one method to acquaint it with the dataElf object it needs to do its job:

sesElf.attach(dataElf)

an method that takes a reference to the dataElf object it should use. First it caches the SES_IN_MEM number (default 200) of least idle sessions. Than it makes more methods available on the sesElf object and returns it. To do its job it uses the dataElf.siftSess() and dataElf.sess(SES_IN_MEM).

sesElf.start(userid)

a method that takes userid number and creates a session for that user. It then returns an object {sid, token} with the id of session created and a token to check its validity. If user with userid isn’t found it returnsfalse. To do its job it first uses the dataElf.user(userid) and gets its timeout property if there is one. Then it generates a random token string and takes a timestamp. Then creates a ses object {userid, tokens: [token], userTimeout: timeout, started: timestamp, checked: timestamp} and adds it to the db via dataElf.addSes(ses) while getting back the id of created record. Then it adds that id to the ses object and stores that object in its sessCache array. If there were SES_IN_MEM sessions in there oldest is deleted.

sesElf.check({sid, token})

an async method that takes a ses object with session id and token and checks if that session exists and if token is valid. Returns new token if session is confirmed and false otherwise. First it looks for the session in question in sessCache then, if one is not found, it looks in db via dataElf.ses(sid). If still not found returns false. If found caches that session and checks if token provided is among ses.tokens. If not again returns false. Otherwise it generates a new token, adds it to the session, updates it in the sessCache and in the db via dataElf.updSes(sid, {ckecked: timestamp, tokens})

sesElf.end(sid)

an async method that takes a sid number and deletes the session with that id from the sessCache and from the db via the dataElf.delSes(sid). Returns true if session was found and deleted and false otherwise.

sesElf.label(sid, label)

an async method that takes a sid number and a label string and names the session found by that id with that label. To do that if looks for the session in sessCache and (if one not found) in the db via dataElf.ses(sid). If one not found it returns false. Otherwise it updates the session in sessCache and in the db via the dataElf.updSes(sid, {label}) and then returns true.

sesElf.limit(sid, timeout)

an async method that takes sid and timeout numbers and sets the idle life limit for the corresponding session (counting from the last check). It disables the limit if timeout is falsy. To do that if looks for the session in sessCache and (if one not found) in the db via dataElf.ses(sid). If one not found it returns false. Otherwise it updates the session in sessCache and in the db via the dataElf.updSes(sid, {timeout}) and then returns true.

sesElf.limitAll(userid, timeout)

an async method that takes userid and timeout numbers and sets the idle life limit for all the sessions current and future for the user with that id. To do that it uses the dataElf.updUser(userid, {timeout}) and then returns its returned value.

passElf

(structure)./pasself/index.js (all files in repo)

A module that checks passwords when it is necessary and allows or denies the execution of the api handler function. It also registers new users and allows the password change if necessary.

It exports a passElf object with one method to acquaint with the dataElf object it needs to do its job:

passElf.attach(dataElf)

a method that takes a reference to the dataElf object it should use. That makes more methods available on the dataElf object and returns it.

passElf.reg(login, pass)

an async method that takes non empty strings login and pass and registers a new user with password hashed. If login is already occupied it should return false. If successful it should return the id of newly made user record. To do its job it supposed to use bcryptjs hashing and dataElf.addUser(login, hash).

passElf.check(id | login, pass)

an async method that takes an id number or a login string and a pass string, checks if it is the correct password according to hash. Returns true if positive, false if not, null if brute force suspected. To do its job it supposed to use bcryptjs comparing, dataElf.user(id | {login}), dataElf.updUser(id, {...props}) and sleep. If checks are too frequent (more then 3 sequential) there are increasing delays per user with locking until ready to check again (up to 5 minutes).

passElf.change(id | login, pass)

an async method that takes an id number or a login string and a pass string, and saves this new password hashed for the user. Returns true if successful, null if id/login not found. To do its job it supposed to use bcryptjs hashing and dataElf.updUser(id | {login}, hash).

dataElf

(structure)./dataelf/index.js (all files in repo)

A module that handles data on its own or by using a database. Multiple convenient methods to run complex queries on the db are supposed to be added to it after linking.

It exports a dataElf object with one method to make it useful:

dataElf.link(dbStr)

an async method that takes a connect string and makes more methods available on the dataElf object to work with the database. It also returns the dataElf object.

dataElf.db

a property reference to the database client object with the corresponding methods to query it directly.

dataElf.addArr(name [,...names])

an async method that adds one or more new arrays (collections) to the database. That is if they are do not exist yet.

dataElf.user(id | {...props})

an async method that returns a record from the users collection, if finds one by id or option property (like login). id supposed to be an integer, options object supposed to have property (or more) with values to find matching record. Returns {id: Number, login: String, hash: String} or null if nothing found.

dataElf.addUser(login, hash)

an async method that adds a record to the users collection in the database. login and hash are supposed to be non empty strings. It should return false if there is already a record with that login. If successful it will return an id of created record.

dataElf.updUser(id | {...props}, {...newProps})

an async method that updates a record in the users collection in the database. It supposed to find the record by id or by properties provided as an object. It should return false if record not found or true if record found and updated.

dataElf.siftSess()

an async method that sifts session records and deletes all that have expired based on last time each was checked, its timeout and user’s timeout.

dataElf.sess(num)

an async method that takes a number and returns that many least idle sessions from the sessions collection in the database.

dataElf.ses(id)

an async method that takes an id number and searches for the session record with that id. It returns that record or false if one is not found.

dataElf.addSes(ses)

an async method that takes a ses object like {userid, tokens: [token], userTimeout: timeout, started: timestamp, checked: timestamp} and adds a record like that with a new id in the sessions collection. It also returns that id.

dataElf.updSes(sid, {...props})

an async method that updates a record in the sessions collection in the db. It supposed to find the record by the sid. It should return false if record not found or true if record found and updated.

dataElf.delSes(sid)

an async method that deletes a record in the sessions collection in the db. It supposed to find the record by the sid. It should return false if record not found or true if record found and deleted.