Reference
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:
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");
| Setting | Default | Info |
| ADMIN_PORT | 5100 | port used by admin web server |
| ADMIN_URL | http:// + DNS_NAME + : + ADMIN_PORT | URL for clients to access admin pages |
| APP_API_DIR | BASE_DIR + /example/apis | directory where developer API modules are located. |
| BASE_DIR | ~/shellyjs | value 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_MATCHER | true | automatically create a matcher process for any game |
| CLUSTER_NUM_ADMIN | 1 | number of admin web workers |
| CLUSTER_NUM_GAMES | 1 | number of example game web workers |
| CLUSTER_NUM_MAILER | 1 | number of send mailer workers |
| CLUSTER_NUM_MATCHER | 1 | number of player matcher workers per game |
| CLUSTER_NUM_REST | 1 | number of REST API workers |
| CLUSTER_NUM_SOCKET | 1 | number of WebSocket API workers |
| CLUSTER_NUM_TCP | 1 | number of TCP API workers |
| CLUSTER_URL | tcp://localhost:5151 | Internal server access URL for cluster communication |
| CONFIG_DIR | BASE_DIR + /config | location of config files |
| DB_LOCK_RETRIES | 5 | number of retries if a lock fails |
| DB_LOCK_SLEEP | 1000 | milliseconds to wait before lock retry |
| DB_OPTIONS | {<DB specific>} | See examples in ~/config/mail.js |
| DB_SCOPE | dev: | Prefix for all DB keys - allows multiple ShellyJS instances to use same DB |
| DB_WRAPPER | /Users/scott/git/shellyjs/lib/db/shsqlite.js | DB to use |
| DEFAULT_ADMIN_NAME | shelly | User name for defaulit admin - created only if doesn't exist |
| DEFAULT_ADMIN_PASSWORD | shelly | Password for default admin |
| DNS_NAME | localhost | External server name that clients use |
| EMAIL_DEFAULT_FROM | Game Shelly | From field in email header |
| EMAIL_NOSEND | true | Dequeue but do not send emails (used for testing) |
| EMAIL_QUEUE | true | Queue emails for sending - false sends synchronous |
| EMAIL_QUEUE_RETRIES | 2 | how many times to resend on failure |
| EMAIL_SENDTO | scott@lgdales.com | send all emails to this address (for testing) |
| EMAIL_TRANSPORT | SMTP | SMTP or SES |
| EMAIL_TRANSPORT_SERVICE | {} | transport specific options |
| FAKE_SESSION_HASH | XXXX | user session hash that is always valid (for testing) |
| FAKE_SESSION_ON | false | allow the fake session hash |
| GAMES_API_DIR | /Users/scott/git/shellyjs/example/games | location of server side game APIs |
| GAMES_PORT | 5102 | port for the example game web server |
| GAMES_URL | http:// + DNS_NAME + : + GAMES_PORT | url for clients to access example games |
| HEART_BEAT | 30000 | keep 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_INTERVAL | 3000 | milliseconds between match queue checks |
| MODULE_CACHE | false | true if API modules are cached, false if they are reloaded each call (used for dev) |
| REG_ALLOW_ANONYMOUS | true | should reg.anonymous be enabled or not |
| REG_RESET_URL | http:// + DNS_NAME + : + GAMES_PORT + /reg/reset.html | url for password reset email |
| REST_PORT | 5101 | port for rest server |
| REST_URL | http:// + DNS_NAME + : + REST_PORT + /api | url for clients to access rest server |
| SERVER_TAG_FN | ~/shellyjs/example/config/server.json | location of servers uuid - created on first run |
| SESSION_PRIVATE_KEY | "<uuid string>" | uuid for users session hash |
| SESSION_TIMEOUT | 0 | seconds session should be valid - 0 is infinit |
| SOCKET_PORT | 5110 | port used for WebSocket server |
| SOCKET_URL | ws:// + DNS_NAME + : + SOCKET_PORT | url for clients to access WebSocket server |
| TCP_PORT | 5111 | port used for TCP server |
| TCP_URL | tcp:// + DNS_NAME + : + TCP_PORT | url for clients to access TCP server |
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.
<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)
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 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"
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);
};
For anyone familiar with Express, this function signature should be familiar. When a command is sent from the client it gets processed and ends up with a call to the API command handler function declared in the module file. This function is passed a Request (req) object, a Response (res) object, and a callback (cb) function.
Given the API commands work against REST, WebSocket, and TCP interfaces only the following members should be used without checking the API type (req.api). The req and res objects under the REST interface have a super set of the member below and those found in Express. The WebSocket and TCP objects are similar.
This object holds all the information about the API command called from the client.
req.api (string): API the client command was issued against ("rest", "socket", "tcp")
req.session (object): Session object if valid.
req.session.user (object): Full user object for the current command. (See shuser.js)
req.session.uid (string): Short cut for user id req.session.user.get("oid")
req.session.valid (boolean): Is there a valid session for this request.
req.session.error (object): Error object with failure information if the session is not valid.
req.loader (object): Object that provides CRUD, locking, and caching for any ShObject subclass (see shobject.js)
req.body (object): Object that contains the parameters passed with the command.
req.env (object): Object for modules to use to pass information between pre, command, and post API module functions.
This object is used to return information to the caller. Responses are queued until after the call completes and any modified data is written back to the datastore - this is important to avoid any race conditions with notifications.
res.add(data (object)): queue an object to be sent back to the client JSON.stringified
res.flush(): send all data objects back to client
res.clear(): remove all data objects from the queue
res.notifyAdd(uids (array), data (object), exUids (string|array)): queue the data object to push to the list of uids if they currently have a Websocket or TCP connections. exUids are removed from the list before sending. exUids can be a single uid string or an array of uid strings. This handles the common case where res.add is sending the message back to the caller and you don't want to double send.
res.notifyFlush(): push all data objects to the target users
res.notifyClear(): remove all push data objects from the queue
cb(err, data): Function to call when done processing the command. If err is returned the command processor will stop processing the message, save any modified data objects, and flush any responses, notifications.
NOTE: error responses should be added via res.add as returning an err via the callback function will only stop command processing.
The following is a slightly more complicated example from the Object API:
object.get = function (req, res, cb) {
req.loader.exists("kObject", req.body.oid, _w(cb, function (err, obj) {
if (err) {
res.add(sh.errordb(obj));
return cb(1);
}
if (obj === null) {
res.add(sh.error("object-get", "unable to get object", obj));
return cb(1);
}
res.add(sh.event("object.get", obj.getData()));
return cb(0);
}));
};