pomelo-upgrade
Version:
387 lines (361 loc) • 10.5 kB
JavaScript
'use strict';
/**
* backend session service for backend session
*/
const utils = require('../../util/utils');
const EXPORTED_FIELDS = ['id', 'frontendId', 'uid', 'settings'];
/**
* Service that maintains backend sessions and the communication with frontend
* servers.
*
* BackendSessionService would be created in each server process and maintains
* backend sessions for current process and communicates with the relative
* frontend servers.
*
* BackendSessionService instance could be accessed by
* `app.get('backendSessionService')` or app.backendSessionService.
*
* @class
* @constructor
*/
class BackendSessionService
{
constructor(app)
{
this.app = app;
}
create(opts)
{
if (!opts)
{
throw new Error('opts should not be empty.');
}
return new BackendSession(opts, this);
}
/**
* Get backend session by frontend server id and session id.
*
* @param {string} frontendId frontend server id that session attached
* @param {string} sid session id
* @param {function} cb callback function. args: cb(err, BackendSession)
*
* @memberOf BackendSessionService
*/
get(frontendId, sid, cb)
{
const namespace = 'sys';
const service = 'sessionRemote';
const method = 'getBackendSessionBySid';
const args = [sid];
rpcInvoke(this.app, frontendId, namespace, service, method,
args, BackendSessionCB.bind(null, this, cb));
}
/**
* Get backend sessions by frontend server id and user id.
*
* @param {string} frontendId frontend server id that session attached
* @param {string} uid user id binded with the session
* @param {function} cb callback function. args: cb(err, BackendSessions)
*
* @memberOf BackendSessionService
*/
getByUid(frontendId, uid, cb)
{
const namespace = 'sys';
const service = 'sessionRemote';
const method = 'getBackendSessionsByUid';
const args = [uid];
rpcInvoke(this.app, frontendId, namespace, service, method,
args, BackendSessionCB.bind(null, this, cb));
}
/**
* Kick a session by session id.
*
* @param {string} frontendId cooperating frontend server id
* @param {number} sid session id
* @param {function} cb callback function
*
* @memberOf BackendSessionService
*/
kickBySid(frontendId, sid, reason, cb)
{
const namespace = 'sys';
const service = 'sessionRemote';
const method = 'kickBySid';
const args = [sid];
if (typeof reason === 'function')
{
cb = reason;
}
else
{
args.push(reason);
}
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
}
/**
* Kick sessions by user id.
*
* @param {string} frontendId cooperating frontend server id
* @param {Number|String} uid user id
* @param {string} reason kick reason
* @param {function} cb callback function
*
* @memberOf BackendSessionService
*/
kickByUid(frontendId, uid, reason, cb)
{
const namespace = 'sys';
const service = 'sessionRemote';
const method = 'kickByUid';
const args = [uid];
if (typeof reason === 'function')
{
cb = reason;
}
else
{
args.push(reason);
}
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
}
/**
* Bind the session with the specified user id. It would finally invoke the
* the sessionService.bind in the cooperating frontend server.
*
* @param {string} frontendId cooperating frontend server id
* @param {number} sid session id
* @param {string} uid user id
* @param {function} cb callback function
*
* @memberOf BackendSessionService
* @api private
*/
bind(frontendId, sid, uid, cb)
{
const namespace = 'sys';
const service = 'sessionRemote';
const method = 'bind';
const args = [sid, uid];
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
}
/**
* Unbind the session with the specified user id. It would finally invoke the
* the sessionService.unbind in the cooperating frontend server.
*
* @param {string} frontendId cooperating frontend server id
* @param {number} sid session id
* @param {string} uid user id
* @param {function} cb callback function
*
* @memberOf BackendSessionService
* @api private
*/
unbind(frontendId, sid, uid, cb)
{
const namespace = 'sys';
const service = 'sessionRemote';
const method = 'unbind';
const args = [sid, uid];
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
}
/**
* Push the specified customized change to the frontend internal session.
*
* @param {string} frontendId cooperating frontend server id
* @param {number} sid session id
* @param {string} key key in session that should be push
* @param {object} value value in session, primitive js object
* @param {function} cb callback function
*
* @memberOf BackendSessionService
* @api private
*/
push(frontendId, sid, key, value, cb)
{
const namespace = 'sys';
const service = 'sessionRemote';
const method = 'push';
const args = [sid, key, value];
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
}
/**
* Push all the customized changes to the frontend internal session.
* @param {string} frontendId cooperating frontend server id
* @param {number} sid session id
* @param {object} settings key/values in session that should be push
* @param {function} cb callback function
*
* @memberOf BackendSessionService
* @api private
*/
pushAll(frontendId, sid, settings, cb)
{
const namespace = 'sys';
const service = 'sessionRemote';
const method = 'pushAll';
const args = [sid, settings];
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
}
}
/**
* BackendSession is the proxy for the frontend internal session passed to handlers and
* it helps to keep the key/value pairs for the server locally.
* Internal session locates in frontend server and should not be accessed directly.
*
* The mainly operation on backend session should be read and any changes happen in backend
* session is local and would be discarded in next request. You have to push the
* changes to the frontend manually if necessary. Any push would overwrite the last push
* of the same key silently and the changes would be saw in next request.
* And you have to make sure the transaction outside if you would push the session
* concurrently in different processes.
*
* See the api below for more details.
*
* @class
* @constructor
*/
class BackendSession
{
constructor(opts, service)
{
// for (const f in opts)
// {
// this[f] = opts[f];
// }
Object.assign(this, opts);
this.__sessionService__ = service;
}
/**
* Bind current session with the user id. It would push the uid to frontend
* server and bind uid to the frontend internal session.
*
* @param {Number|String} uid user id
* @param {function} cb callback function
*
* @memberOf BackendSession
*/
bind(uid, cb)
{
this.__sessionService__.bind(this.frontendId, this.id, uid, err =>
{
if (!err)
{
this.uid = uid;
}
utils.invokeCallback(cb, err);
});
}
/**
* Unbind current session with the user id. It would push the uid to frontend
* server and unbind uid from the frontend internal session.
*
* @param {Number|String} uid user id
* @param {function} cb callback function
*
* @memberOf BackendSession
*/
unbind(uid, cb)
{
this.__sessionService__.unbind(this.frontendId, this.id, uid, err =>
{
if (!err)
{
this.uid = null;
}
utils.invokeCallback(cb, err);
});
}
/**
* Set the key/value into backend session.
*
* @param {string} key key
* @param {object} value value
*/
set(key, value)
{
this.settings[key] = value;
}
/**
* Get the value from backend session by key.
*
* @param {string} key key
* @return {object} value
*/
get(key)
{
return this.settings[key];
}
/**
* Push the key/value in backend session to the front internal session.
*
* @param {string} key key
* @param {function} cb callback function
*/
push(key, cb)
{
this.__sessionService__.push(this.frontendId, this.id, key, this.get(key), cb);
}
/**
* Push all the key/values in backend session to the frontend internal session.
*
* @param {function} cb callback function
*/
pushAll(cb)
{
this.__sessionService__.pushAll(this.frontendId, this.id, this.settings, cb);
}
/**
* Export the key/values for serialization.
*
* @api private
*/
export()
{
const res = {};
EXPORTED_FIELDS.forEach(function(field)
{
res[field] = this[field];
});
return res;
}
}
const BackendSessionCB = function(service, cb, err, sinfo)
{
if (err)
{
utils.invokeCallback(cb, err);
return;
}
if (!sinfo)
{
utils.invokeCallback(cb);
return;
}
let sessions = [];
if (Array.isArray(sinfo))
{
// #getByUid
for (let i = 0, k = sinfo.length; i < k; i++)
{
sessions.push(service.create(sinfo[i]));
}
}
else
{
// #get
sessions = service.create(sinfo);
}
utils.invokeCallback(cb, null, sessions);
};
const rpcInvoke = function(app, sid, namespace, service, method, args, cb)
{
app.rpcInvoke(sid, {namespace : namespace,
service : service,
method : method,
args : args}, cb);
};
module.exports = function(app)
{
return new BackendSessionService(app);
};