<%- include nav.html %>

ShellyJS

Reference

Approach

Configuration settings are stored at the global scope under the "global.C" object and accessed via "global.C.KEY_NAME"

var sh = require(global.C.BASE_DIR + "/lib/shutil.js");

At startup ShellyJS will look for a directory named "config" in the shellyjs install directory "./node_modes/shellyjs/config"

This is typically overridden by passing in "CONFIG_DIR" to the shellyjs.start function.

shelly.start({
  CONFIG_DIR: __dirname + "/config"
});

Configuration values are set via the following in priority order:

  1. Passed on the command line via "key"="value"
  2. Passed as key:values in the first object parameter the shellyjs.start function
  3. Set using global.CDEF() or global.CFDEF() functions in a configuration file (keys.js, os.hostname() + ".js", main.js

global.CDEF("KEY", <VALUE>): Define if not yet defined. Value can be any valid variable type.

global.CFDEF("KEY", <VALUE>): Force the define even if defined already.

To keep things segmented well you can chain your "main.js" configuration file to the default one like:

global.CDEF("MODULE_CACHE", false);
global.CDEF("CLUSTER_NUM_SOCKET", 1);

// pick up all the other default configs
require(global.C.BASE_DIR + "/config/main.js");

Settings

SettingDefaultInfo
ADMIN_PORT5100port used by admin web server
ADMIN_URLhttp:// + DNS_NAME + : + ADMIN_PORTURL for clients to access admin pages
APP_API_DIRBASE_DIR + /example/apisdirectory where developer API modules are located.
BASE_DIR~/shellyjsvalue is always install directory and cannot be changed
CLUSTER
{
    "socket": {src: "/src/socket.js", num: global.C.CLUSTER_NUM_SOCKET, args: ["websocket"]},
    "tcp": {src: "/src/socket.js", num: global.C.CLUSTER_NUM_TCP, args: ["tcp"]},
    "rest": {src: "/src/rest.js", num: global.C.CLUSTER_NUM_REST, args: null},
    "admin": {src: "/src/admin.js", num: global.C.CLUSTER_NUM_ADMIN, args: null},
    "games": {src: "/src/games.js", num: global.C.CLUSTER_NUM_GAMES, args: null},
    "mailer": {src: "/lib/shmailer.js", num: global.C.CLUSTER_NUM_MAILER, args: null}
}
Info: These are the workers started and maintained by the master.
CLUSTER_AUTO_GAME_MATCHERtrueautomatically create a matcher process for any game
CLUSTER_NUM_ADMIN1number of admin web workers
CLUSTER_NUM_GAMES1number of example game web workers
CLUSTER_NUM_MAILER1number of send mailer workers
CLUSTER_NUM_MATCHER1number of player matcher workers per game
CLUSTER_NUM_REST1number of REST API workers
CLUSTER_NUM_SOCKET1number of WebSocket API workers
CLUSTER_NUM_TCP1number of TCP API workers
CLUSTER_URLtcp://localhost:5151Internal server access URL for cluster communication
CONFIG_DIRBASE_DIR + /configlocation of config files
DB_LOCK_RETRIES5number of retries if a lock fails
DB_LOCK_SLEEP1000milliseconds to wait before lock retry
DB_OPTIONS{<DB specific>}See examples in ~/config/mail.js
DB_SCOPEdev:Prefix for all DB keys - allows multiple ShellyJS instances to use same DB
DB_WRAPPER/Users/scott/git/shellyjs/lib/db/shsqlite.jsDB to use
DEFAULT_ADMIN_NAMEshellyUser name for defaulit admin - created only if doesn't exist
DEFAULT_ADMIN_PASSWORDshellyPassword for default admin
DNS_NAMElocalhostExternal server name that clients use
EMAIL_DEFAULT_FROMGame Shelly From field in email header
EMAIL_NOSENDtrueDequeue but do not send emails (used for testing)
EMAIL_QUEUEtrueQueue emails for sending - false sends synchronous
EMAIL_QUEUE_RETRIES2how many times to resend on failure
EMAIL_SENDTOscott@lgdales.comsend all emails to this address (for testing)
EMAIL_TRANSPORTSMTPSMTP or SES
EMAIL_TRANSPORT_SERVICE{}transport specific options
FAKE_SESSION_HASHXXXXuser session hash that is always valid (for testing)
FAKE_SESSION_ONfalseallow the fake session hash
GAMES_API_DIR/Users/scott/git/shellyjs/example/gameslocation of server side game APIs
GAMES_PORT5102port for the example game web server
GAMES_URLhttp:// + DNS_NAME + : + GAMES_PORTurl for clients to access example games
HEART_BEAT30000keep alive message interval for WebSocket and TCP interfaces
LOGIN_PRIVATE_KEY"<uuid string>"uuid for login password hash
LOG_CONSOLE_OPTS{}winston options for console logging
LOG_FILE_OPTS{}winston optinos for file logging
LOG_HOOK
function (winston) {
  winston.add(winston.transports.Console, global.C.LOG_CONSOLE_OPTS);
  winston.add(winston.transports.File, global.C.LOG_FILE_OPTS);
}
Info: hook called after winston init to set any options
LOG_MODULES{"<type>":1, ...}types of messages to log (1 on, 0 off) - first param of shlog.info("<type>",...)
MATCHER_INTERVAL3000milliseconds between match queue checks
MODULE_CACHEfalsetrue if API modules are cached, false if they are reloaded each call (used for dev)
REG_ALLOW_ANONYMOUStrueshould reg.anonymous be enabled or not
REG_RESET_URLhttp:// + DNS_NAME + : + GAMES_PORT + /reg/reset.htmlurl for password reset email
REST_PORT5101port for rest server
REST_URLhttp:// + DNS_NAME + : + REST_PORT + /apiurl for clients to access rest server
SERVER_TAG_FN~/shellyjs/example/config/server.jsonlocation of servers uuid - created on first run
SESSION_PRIVATE_KEY"<uuid string>"uuid for users session hash
SESSION_TIMEOUT0seconds session should be valid - 0 is infinit
SOCKET_PORT5110port used for WebSocket server
SOCKET_URLws:// + DNS_NAME + : + SOCKET_PORTurl for clients to access WebSocket server
TCP_PORT5111port used for TCP server
TCP_URLtcp:// + DNS_NAME + : + TCP_PORTurl for clients to access TCP server

Overview

Location

The API modules are created in directories under APP_API_DIR directory set in the configuration. The directory and file names are important as they are used to define the calling signature of the api.

If you wanted to call a function "example.hello" you would create the file "~/apis/example/example.js". Assuming the APP_API_DIR is set to "~/apis". This API will then show up under the "App APIs" tab in the admin and be available via REST, WebSocket, and TCP interfaces.

Module Structure

<API_MODULE>.desc: type: string, simple description of what API does (displayed in admin)

<API_MODULE>.functions: type: object, list of commands that the API supports

Each function is a defined as a member of the object with the value being an object with the list of options.

<API_MODULE>.functions.<COMMAND>.desc: type: string, short discription of what this command does

<API_MODULE>.functions.<COMMAND>.params: type: object, members are the parameters required by the command

<API_MODULE>.functions.<COMMAND>.params.<PARAM_NAME>: type: object, member name is the name of parameter

<API_MODULE>.functions.<COMMAND>.params.<PARAM_NAME>.dtype: type: sting, javascript type for parameter (string, object, array, number, boolean)

<API_MODULE>.functions.<COMMAND>.security: type: array, list of strings that are the roles required to access this command

<API_MODULE>.functions.<COMMAND>.noSession: type: boolean, true if no session is required to access this command (defaults to false)

Example

var sh = require(global.C.BASE_DIR + "/lib/shutil.js");

var example = exports;

example.desc = "example api";
example.functions = {
  hello: {desc: "simple hello world api example", params: {}, security: []},
  echo: {desc: "one parameter api example", params: {param1: {dtype: "string"}}, security: []}
};

example.hello = function (req, res, cb) {
  res.add(sh.event("example.hello", "world"));
  return cb(0);
};

example.echo = function (req, res, cb) {
  res.add(sh.event("example.echo", req.body.param1));
  return cb(0);
};

Line By Line:

Line 1: Not required but has helper functions like sh.event and sh.error provide a consistent envelope for responses.

Line 3: All members of "example" are exported.

Line 5: Set the general descriptions for the module.

Line 6-9: Function definitions that are to be used in the API as commands

Line 7: Simple "hello" command that takes no parameters

line 8: "echo" command that takes one parameter "param1" that must be a string

Line 11: Implementation of the "hello" function that is passed the request (req), response (res), and callback (cb) arguments

Line 12: Add an message to the response stream by calling "res.add()". Any object can be added and will be sent via JSON.stringify. These examples use the "sh.event()" function to provide a consistent envelope.

Line 13: Make the callback with "0" for no errors

Line 16-19: Implementation of the "echo" function

Line 17: Adds to the message to the response object by accessing the "param1" from the "request.body"

<%- include footer.html %>