Actionhero is a realtime multi-transport nodejs API Server with integrated cluster capabilities and delayed tasks
APACHE-2.0 License
Bot releases are hidden (Show)
Published by evantahler over 9 years ago
You can now return an error in an initializer to the callback. Doing so will crash the actionhero process and log the error. You can do this in any step of the initializer (start
, stop
, initialize
).
start()
methods of servers.We now have a trace
level of logging which is lower than debug
. Many of the resque logs have been moved to this level.
istanbul
library to test code-coverage from travis.ciPublished by evantahler over 9 years ago
startCluster
, and stop the cluster when this happens
"."
Published by evantahler over 9 years ago
()
in regular expressions within routes (https://github.com/evantahler/actionhero/pull/573 by @keifukuda)say
command with the params in random order (which should be allowed) (https://github.com/evantahler/actionhero/pull/577 by @evantahler)api.cache
object at boot
api.cache.size
when needed to checkPublished by evantahler almost 10 years ago
Rename erroneous self
reference in client js
Published by evantahler almost 10 years ago
Published by evantahler almost 10 years ago
Fix a poorly named path which prevented actionhero generate
from functioning.
Published by evantahler almost 10 years ago
defaults
, validation
, formatting
, and requirements
in an action's inputs.say
within chatrooms, better blocking callbacks for roomAdd
and roomLeave
(and the depreciation of the previous authentication APIs)module.exports = {
loadPriority: 1000,
startPriority: 1000,
stopPriority: 1000,
initialize: function(api, next){
api.%%name%% = {};
next();
},
start: function(api, next){
next();
},
stop: function(api, next){
next();
}
}
module.exports
)initialize
, start
, and stop
.
_start
and _stop
methods have been moved to the top level (and renamed to start
and stop
), along with the initialize
method.api
object and a callback.loadPriority
(system levels are > 1000)startPriority
(system levels are > 1000)stopPriority
(system levels are > 1000)sayCallbacks
. This allows you to modify messages being sent to clients, log all messages your self, etc. It is similar in nature to the joinCallbacks
and leaveCallbacks
.var chatMiddlewareToAnnounceNewMembers = function(connection, room, callback){
api.chatRoom.broadcast({}, room, 'I have entered the room: ' + connection.id, function(e){
callback();
});
}
api.chatRoom.addJoinCallback(chatMiddlewareToAnnounceNewMembers, 100);
var chatMiddlewareToAnnounceGoneMembers = function(connection, room, callback){
api.chatRoom.broadcast({}, room, 'I have left the room: ' + connection.id, function(e){
callback();
});
}
api.chatRoom.addLeaveCallback(chatMiddlewareToAnnounceGoneMembers, 100);
var middlewareToAddSimleyFacesToAllMessages = function(connection, room, messagePayload, callback){
messagePayload.message = messagePayload.message + ' :)';
callback(null, messagePayload);
}
api.chatRoom.addSayCallback(middlewareToAddSimleyFacesToAllMessages, 100);
{}
connection is used to proxy the message coming from the 'system'sayCallbacks
have a second return value on the callback, messagePayload
. This allows you to modify the message being sent to your clients.messagePayload
will be modified and and passed on to all addSayCallback
middlewares inline, so you can append and modify it as you gosayCallbacks
, joinCallbacks
or leaveCallbacks
), the priority maters, and you can block subsequent methods from firing by returning an error to the callback.// in this example no one will be able to join any room, and the broadcast callback will never be invoked.
api.chatRoom.addJoinCallback(function(connection, room, callback){
callback(new Error('blocked from joining the room'));
}, 100);
api.chatRoom.addJoinCallback(function(connection, room, callback){
api.chatRoom.broadcast({}, room, 'I have entered the room: ' + connection.id, function(e){
callback();
});
}, 200);
sayCallback
is blocked/errored, the message will simply not be delivered to the client. If a joinCallbacks
or leaveCallbacks
is blocked/errored, the verb or method used to invoke the call will be returned that error.api.chatRoom.generateMemberDetails(connection)
api.chatRoom.sanitizeMemberDetails(memberData)
api.chatRoom.roomStatus(room,callback)
.api.chatRoom.generateMessagePayload(message)
You can finally provide defaults
, validation
, formatting
, and requirements
in an action's inputs.
This is a breaking change.
exports.sleepTest = {
name: 'sleepTest',
description: 'I will sleep and then return',
inputs: {
sleepDuration: {
required: true,
formatter: function(n){ return parseInt(n); },
default: function(){ return 1000; }
validator: function(n){
if(n < 1000){ return 'sleepDuration should be longer than 1 second'; }
else{ return true; }
}
}
},
run: function(api, connection, next){
setTimeout(function(){
next(connection, true);
}, connection.params.sleepDuration)
})
}
The options are:
required
(boolean)formatter = function(param, connection, actionTemplate)
default = function(param, connection, actionTemplate)
default
father than a function, ie: default: 123
validator = function(param, connection, actionTemplate)
true
if validation passedrequired
and optional
.inputs
definedoutputExample
definedapi.config.general.missingParamChecks = [null, '', undefined]
to choose explicitly how you want un-set params to be handled in your actions. For example, if you want to allow explicit null
values in a JSON payload but not undifined
, you can now opt-in to that behavior.
exports.default = {
tasks: function(api){
return {
// Should this node run a scheduler to promote delayed tasks?
scheduler: false,
// what queues should the workers work?
queues: ['*'],
// how long to sleep between jobs / scheduler checks
timeout: 5000,
// at minimum, how many parallel taskProcessors should this node spawn?
// (have number > 0 to enable, and < 1 to disable)
minTaskProcessors: 0,
// at maximum, how many parallel taskProcessors should this node spawn?
maxTaskProcessors: 0,
// how often should we check the event loop to spawn more workers?
checkTimeout: 500,
// how many ms would constitue an event loop delay to halt worker spawning?
maxEventLoopDelay: 5,
// When we kill off a taskProcessor, should we disconnect that local redis connection?
toDisconnectProcessors: true,
// What redis server should we connect to for tasks / delayed jobs?
redis: api.config.redis
}
}
}
api.tasks.scheduledAt(q, taskName, args, callback)
api.tasks.timestamps(callback)
api.tasks.delayedAt(timestamp, callback)
api.tasks.allDelayed(callback)
api.tasks.workers(callback)
api.tasks.queue.workingOn(workerName, queues, callback)
api.tasks.queue.allWorkingOn(callback)
api.tasks.details
(via api.resque.queue.allWorkingOn
) and is returned in the generated status
action in new projects.roomAdd
and roomLeave
. This is required now that these methods can fail async.api.config.servers.websocket.destroyClientsOnShutdown = false
controls this behavior.client.on('error') = function(err)
client.on('reconnect') = function()
client.on('reconnecting') = function()
_start()
to start()
and _stop()
to stop()
initialize
regardless of server nameWe support now multiple configuration paths as follows:
Note that if --config or ACTIONHERO_CONFIG are used, they overwrite the use of the default "config" folder. If you wish to use both, you need to re-specify "config", e.g. "--config=config,local-config". Also, note that specifying multiple --config options on the command line does exactly the same thing as using one parameter with comma separators, however the environment variable method only supports the comma-delimited syntax.
Via https://github.com/evantahler/actionhero/pull/541 by @crrobinson14
config/api.js
to a new config/plugins.js
--plugins
actiondomains
renamed to actionDomains
api.actionheroVersion
. The generated status
action in new projects will return this value.Published by evantahler almost 10 years ago
connection.rawConnection.responseHeaders
(https://github.com/evantahler/actionhero/pull/502)Published by evantahler almost 10 years ago
Release v9.4.0 of actionhero is all about getting out code up-to-code! We spent the time to lint
our js and integrate with test coverage tools
You can also now check out the actionhero project's coverage on codecliamte
In order to shape up, there are a few breaking changes:
connection._original_connection
becomes connection._originalConnection
api.chatRoom.reAuthenticate
now includes error
as first argument, then the array of rooms that failed.actionheroClient
becomes ActionheroClient
, however, the old name is aliased for backwards compatibility.actionhero has included domains
for a while now. Domains allow you to wrap an entire request so that any uncaught errors can be handled (ie: return a 500 to the client, and not crash the application). This a good node defensive design pattern, but domains have some heavy overhead. Starting in this release, the use of domains is now optional, and configureable with api.config.general.actionDomains
Keep in mind that if you disable api.config.general.actionDomains
, your application is likely to preform faster under load, but can be crashed by a single bad request. The choice is yours!
Published by evantahler about 10 years ago
In this commit we have removed the 'auto-adding' of params from routes. Previously, if you listed a variable in a route (IE: /users/:userID
) and you did not include userID
as an input to your action, it would still have been possible to accept connection.params.userID
in your action. Removing this feature makes actions safer and ensures that you truly only are getting the params you define into an action.
If you run into trouble with this update, be sure that your actions list all the params they expect!
See the conversation here for more details https://github.com/evantahler/actionhero/issues/481
Added Gitter
Gitter is a chat tool based around github. I've made a gitter room for actionhero we can all chat in. This deprecates the old, rarely used IRC room. Gitter has mobile apps, desktop apps, and a good website with chat persistence. Like IRC, it is free.
We have updated all of actionhero's dependent packages to their latest versions (except for winston because of this issue)
This small release fixes a few bugs and docs:
Published by evantahler about 10 years ago
v9.3.1 is the very definition of a point release.
DEBUG
was annoying and scary.Published by evantahler about 10 years ago
This release is packed full of updates aimed to make your job as an actionhero developer easier! A huge shout-out is due to @PhilWaldmann who completed the majority of the work in this release with 4 pull requests which added up to a substantial upgrade of the config system.
local project
, plugin
, actionhero core
routes.js
inside of /config
(https://github.com/evantahler/actionhero/pull/452)api.config.general.paths.public
.api.cache.lock
a key, and then save it to prevent other nodes from modifying the object.api.cache.save(key, expireTimeMS, next)
expireTimeMS
is optional, and will be expireTimeMS = api.cache.lockDuration = api.config.general.lockDuration
next(error, lockOk)
error
will be null unless there was something wrong with the connection (perhaps a redis error)lockOk
will be true
or false
depending on if the lock was obtained.api.cache.save(key, next)
next(error, lockOk)
error
will be null unless there was something wrong with the connection (perhaps a redis error)lockOk
will be true
or false
depending on if the lock was removed.api.cache.save(key,retry, next)
retry
is either null
or an integer (ms) that we should keep retrying until the lock is free to be re-obtainednext(error, lockOk)
error
will be null unless there was something wrong with the connection (perhaps a redis error)lockOk
will be true
or false
depending on if the lock is currently obtainable.node-resque
, redis
, and primus
to their latest versionsPublished by evantahler about 10 years ago
This release is comprised of updates entirely from our growing actionhero community!
config/errors.js
/config/servers/web.js
has new settings.Published by evantahler about 10 years ago
This release is a hotfix to re-allow nested routes for api.config.servers.web.urlPathForActions
and api.config.servers.web.urlPathForFiles
. You can once again set them to /path/to/public
Published by evantahler about 10 years ago
Updated the error reporter to provide more details to any api.exceptionHandlers.reporters
. Reporters now will receive (err, type, name, objects, severity)
.
Documentation updates accordingly: http://actionherojs.com/docs/core/exceptions.html#custom-error-reporters
err
is the error object (which you can extract err.stack
from)type
is the type of method which crashed ('action', 'task', etc)name
will be a human-readable name, IE: action:randomNumber
objects
is a hash which contains the relevant objects.
{connection: connection}
{task: task, queue: queue}
severity
will be a logger severity level, like "error" or "alert".Published by evantahler about 10 years ago
To upgrade to this release, there are configuration changes required
This release also more harshly enforces some previously lax URL to Route matching. Your application behavior may change
/api/:action
), this logic is now moved into the routerapi.config.servers.web.simpleRouting
/?action=:action
), this logic is now optional via api.config.servers.web.queryRouting
/api/thing/stuff
matches {path: '/thing/:value', action: 'myAction'}
but will not match {path: '/thing', action: 'myOtherAction'}
/public?file=simple.html
. The URL path must math the file system path (from /public
)action
will not be assumed via the URLfingerprint
to be a top-level connection element (connection.fingerprint
)
connection.rawConnection.fingerprint
connection.fingerprint
to websocket connections whenever possible via api.config.servers.web.fingerprint.cookieKey
/public/linkedSession.html
is provided to demonstrate/config/servers/web.js
api.config.servers.web.returnErrorCodes
now defaults to true
GET
to the root API endpoint will no longer return documentation about the API.showDocumentation
action has been created, and will be generated along with each new projectapi.config.servers.web.urlPathForFiles = null
api.config.general.paths = null
site.com/%/thing
(note that %
was not htmlEncoded)Published by evantahler over 10 years ago
A fix for the way we connect to redis with authentication.
Published by evantahler over 10 years ago
EventEmitter
provided by Primus rather than rolling our ownapiPath
options for HTTP fallback in actionheroClient
actionProcessor
(thanks @joezg)Published by evantahler over 10 years ago
This release is a collection of bug fixes:
api.log
can use Winston's string interpolationPublished by evantahler over 10 years ago
This release focuses on performance, the chat system, and developer tools. We have been listening to your thoughts in the mailing list and on GitHub, and hopefully this release clears up a lot of the confusion and pain points you have identified!
In v9.0.0, the chat system has been gutted and re-written to provide your API with finer controls about chat rooms. Most importantly, you can now control which rooms connections are members of directly. Connections can continue opt to join and leave rooms on their own (assuming the authenticationPattern is met).
connection
object, and the client-facing JS library.say
now require a room name, IE: say myRoom Hello World
over telnet and the socket
server, or client.say(room, message, callback)
in the websocket clientactionHeroClient.js
(and .min) to reflect these changesroomAdd room
and roomLeave room
api.chatRoom.add(room, callback)
api.chatRoom.destroy(room, callback)
api.chatRoom.exists(room, callback)
api.chatRoom.setAuthenticationPattern(room, key, value, callback)
api.chatRoom.roomStatus(room, callback)
api.chatRoom.authorize(connection, room, callback)
api.chatRoom.reAuthenticate(connectionId, callback)
api.chatRoom.addMember(connectionId, room, callback)
api.chatRoom.removeMember(connectionId, room, callback)
In this release, we have removed our dependency on faye in favor of Primus. We now use Primus in the websocket transport, and have moved all backend cluster-cluster communication to raw redis Pub/Sub.
The Primus project allows you to choose from many webscoket backends, including ws
, engine.io
, socket.io
, and more. A number of new options have been added to /config/servers/websocket.js
to manage this. Check out the Primus project for more information.
actionhero will no longer attempt to manage non-sticky client connections. This means if you have a multi-server actionhero deployment and you use long-polling in your websocket transport, you will need to ensure that your load balancer can enforce sticky connections, meaning every request from the client will hit the same actionhero node.
actionheroClient.js
, meaning the methods should all behave the same. However, there have been significant changes under the hood.ws
package as the backend for Primius (so we can generate a working project), but you do not need to keep this package.actionhero generate
will no longer create the client-facing actionheroClient.js
on generation. Rather, the server will re-generate these files on boot each time. This is done so you can make changes in /config/servers/webscoket.js
and have them included into the client JS. 3 new config options help mange the creation of these files:// you can pass a FQDN here, or function to be called / window object to be inspected
clientUrl: 'window.location.origin',
// Directory to render client-side JS.
// Path should start with "/" and will be built starting from api.config..general.paths.public
clientJsPath: 'javascript/',
// the name of the client-side JS file to render. Both `.js` and `.min.js` versions will be created
// do not include the file exension
// set to `null` to not render the client-side JS on boot
clientJsName: 'actionheroClient',
To enable the new chat API above, a key feature was the ability to add connections to a room using "serverA"'a API, even though the connection in question might not be connected to "serverB". This required the creation of a robust Remote Procedure Call (RPC) to allow actionhero servers to communicate with each other.
You can call an RPC to be called on all nodes you may have in your cluster or just a node which holds a specific connection. You can call RPC methods with the new api.redis.doCluster
method. If you provide the optional callback, you will get the first response back (or a timeout error). RPC calls are invoked with api.redis.doCluster(method, args, connectionId, callback)
.
For example, if you wanted all nodes to log a message, you would do: api.redis.doCluster('api.log', ["hello from " + api.id]);
If you wanted the node which holds connection abc123
to change their authorized
status (perhaps because your room authentication relies on this), you would do:
api.connections.apply('abc123', 'set', ['auth', true], function(err){
// do stuff
});
Two new options have been added to the config/redis.js
config file to support this:
// Which channel to use on redis pub/sub for RPC communication
channel: 'actionhero',
// How long to wait for an RPC call before considering it a failure
rpcTimeout: 5000,
RPC calls are authenticated against api.config.serverToken
and communication happens over redis Pub/Sub. BE CAREFUL, as you can call any method within the API namespace on an actionhero server, including shutdown() and read any data on that node.
The new api.connections.apply(connectionId, method, args, callback)
has been introduced. This allows any node in the cluster to modify a property of a connection, even one that isn't located locally on this specific node. This uses the RPC tooling described above under the hood.
PATCH
HTTP verb (thanks @omichowdhury). This verb can also now be used in routes.connection.rawConnection.params.body
and connection.rawConnection.params.files
are available within middleware and actions (Thanks @omichowdhury)callback
param will only convert the response to JSONp (application/javascript
) if the header would have still been x/json
application/json
or application/javascript
, the server will no longer attempt to JSON.stringify
the connection.response
.
connection.rawConnection.responseHeaders.push(['Content-Type', 'text/plain']);
) (thanks @enginespot)connectionHasHeader()
for extractHeader()
which will return the most recent header of a given nameThanks to @innerdvations, you can now choose how to order the execution of your middleware (preProcessor and postProcessor, and connection callbacks). You should no longer push to those arrays (although your application won't error). You should now use api.actions.addPreProcessor(function, priority)
and api.actions.addPostProcessor(function, priority)
for actions and api.connections.addCreateCallback(function, priority)
and api.connections.addDestroyCallback(function, priority)
for connections.
The priority
in all the above is optional, and if not provided, the new api.config.general.defaultProcessorPriority
will be used (defaults to 100).
Per a discussion on the mailing list, we have removed any automatic messaging actionhero might do for the chatrooms in favor of another type of middleware, chat middleware! This middleware allows you to control the messages and actions taken when clients join or leave a chat room.
This should not be used for authentication.
As we do not want to block the ability for a connection to join a room (we already have authentication tools in place), Chat Middleware does not have a callback and is executed "in parallel" to the connection actually joining the room. This middleware can be used for announcing members joining and leaving to other members in the chat room or logging stats.
Use api.chatRoom.addJoinCallback(function(connection, room))
to add a Join Callback, and use api.chatRoom.addLeaveCallback(function(connection, room)
to handle connections leaving a room.
You can optionally provide a priority
to control the order of operations in the middleware.
You can announce to everyone else in the room when a connection joins and leaves:
api.chatRoom.addJoinCallback(function(connection, room){
api.chatRoom.broadcast(connection, room, 'I have entered the room');
});
api.chatRoom.addLeaveCallback(function(connection, room){
api.chatRoom.broadcast(connection, room, 'I have left the room');
});
actionhero now has a REPL! This means you can 'connect' to a running instance of actionhero and manually call all the methods on the api
namespace. This combined with the new RPC tools make this a powerful debugging and development tool. Running grunt console
will load up a version of action hero in your terminal where you have access to the api
object. This version of the server will boot
, initialize
, and start
, but will skip booting any servers
.
The REPL will:
NODE_ENV
properly to load the configIf you are familiar with rails, this is very similar to rails console
Many of you have asked for the ability to change the string
error messages actionhero uses. Perhaps english isn't your user's language, or you want so say something custom. Either way, there's a new config file just for this: config/errors.js
. Each error message is represented by a synchronous function which should return a string. Some functions are passed variables (like the connection) so you can customize your message.
Over the past few months, a great conversation has been happening on GitHub about actionhero speed & performance. This conversation has lead to a few small tweaks inside actionhero which have made a big difference. Most importantly, somewhere between v7.0.0.
and v8.0.2
we changed the async-ness
of the actionProcessor and cache system to rely on setImmediate
rather than process.nextTick
. This change made the seder less susceptible to crashing under heavy load, but cost us significantly in speed. This change was too costly and has since been reverted.
Thank you to everyone who contributed to the conversation!
The change to Priums not only allows for more flexibility in the websocket server, but in preliminary tests, preforms much better than faye.
A list of things to watch out for when upgrading to v9.0.0
form v8.x.x
:
v0.10.0
/config/redis.js
has new options. These new options are required for the RPC system./config/faye.js
/config/api.js
, defaultProcessorPriority : 100,
/config/errors.js
is required./config/servers/websocket.js
to manage this. Check out the Primus project for more information.api.utils.hashMerge
/ api.utils.isPlainObject
have been updated to check provided hashes (or nested hashes) for a special key, _toExpand
. If this key is false
, this object will not be expanded on merge, and copied over literally.connection.actionStatus
if a connection processes an action
true
, or false
true
mean the action ran, but there still may be a connection.error
v0.8.0
, which allows errors to be caught and suppressed via plugin