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 8 years ago
connection.fingerprint === connection.id
unless otherwise specified (AKA: The Web Server). This will ensure that authentication schemes you build should work for all server types.> curl "http:// localhost:8080/api/status"
{
"nodeStatus": "Node Healthy",
"problems": [],
"id": "10.0.1.34",
"actionheroVersion": "14.0.10",
"uptime": 18863,
"name": "actionhero",
"description": "actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks",
"version": "14.0.10",
"consumedMemoryMB": 32.9,
"eventLoopDelay": 0.009,
"resqueTotalQueueLength": 1,
"serverInformation": {
"serverName": "actionhero",
"apiVersion": "14.0.10",
"requestDuration": 29,
"currentTime": 1469219570931
},
"requesterInformation": {
"id": "ff9a4d458b08c3f0488ac7ddba77f239bc56e824-4691655f-cc87-4504-baa1-04e9aa13117d",
"fingerprint": "ff9a4d458b08c3f0488ac7ddba77f239bc56e824",
"remoteIP": "127.0.0.1",
"receivedParams": {
"action": "status",
"apiVersion": 1
}
}
}
should
updated to version ^10.0.0
Published by evantahler over 8 years ago
api.id
will now be appended to log files via default. In older projects, you can update this behavior by changing the format of the timestamp
option in your logger, I.E.:// in config.js
return new (winston.transports.File)({
filename: api.config.general.paths.log[0] + '/' + api.pids.title + '.log',
level: 'info',
timestamp: function(){ return api.id + ' @ ' + new Date().toISOString(); },
});
async
updated to version 2.0Published by evantahler over 8 years ago
*** ActionHero VERB ***
process.env.PWD
as a possible source of the project's working directory at boot (see https://github.com/nodejs/node/issues/7545 for more information)Published by evantahler over 8 years ago
Expose resque functions via api.tasks:
api.tasks.delQueue(queue, next)
api.tasks.queued(q, start, stop, next)
Published by evantahler over 8 years ago
You can now inspect the stats form your resque cluster (failed, succeeded, etc). api.tasks.details
has been extended to include the output of api.tasks.stats
Published by evantahler over 8 years ago
*** ActionHero Started***
rather than ***Server Started***
to remove confusion when running actionhero without any servers (or in console mode)Published by evantahler over 8 years ago
Last-Modified
file headers. If you run actionhero on a computer where English or a western-style calendar is not your default locale, the format of Date().toString()
might not be compliant. Strict checks for this were introduced in node v4.3.0
. More detail here http://www.alexkras.com/typeerror-the-header-content-contains-invalid-characters
ws
package upgraded to fix security issuesPublished by evantahler over 8 years ago
Published by evantahler over 8 years ago
Published by evantahler over 8 years ago
Allows for action validators and formatters to use both named methods and direction functions.
exports.cacheTest = {
name: 'cacheTest',
description: 'I will test the internal cache functions of the API',
outputExample: {},
inputs: {
key: {
required: true,
formatter: [
function(s){ return String(s); },
'api.formatter.uniqueKeyName' // <----------- HERE
},
value: {
required: true,
formatter: function(s){ return String(s); },
validator: function(s){
if(s.length < 3){ return '`value` should be at least 3 letters long'; }
else{ return true; }
}
},
},
run: function(api, data, next){
// ...
}
};
And then you would define an initializer with your formatter:
'use strict';
module.exports = {
initialize: function(api, next){
api.formatter = {
uniqueKeyName: function(key){
return key + '-' + this.connection.id;
}
};
next();
},
};
There are so many ways to configure redis these days... handling the config options for all of them (sentinel? cluster?) is a pain... so lets just let the users configure things directly. It will be so much simpler!
config/redis.js
, you now define the 3 redis connections you need explicitly rather than passing config options around:var host = process.env.REDIS_HOST || '127.0.0.1';
var port = process.env.REDIS_PORT || 6379;
var database = process.env.REDIS_DB || 0;
exports['default'] = {
redis: function(api){
var Redis = require('ioredis');
return {
'_toExpand': false,
// create the redis clients
client: Redis.createClient(port, host),
subscriber: Redis.createClient(port, host),
tasks: Redis.createClient(port, host),
};
}
};
api.config.redis.channel
to api.config.general.channel
api.config.redis. rpcTimeout
to api.config.general. rpcTimeout
api.config.redis.client
rather than api.redis.client
Published by evantahler over 8 years ago
ENABLE_TCP_SERVER
to enable TCP (port 5000) at booti18n
updated to 0.8.3ioredis
updated to 2.1.0uglify-js
updated to 2.6.4Published by evantahler over 8 years ago
In new projects, we will load api.config.general.apiVersion
and api.config.general.serverName
from the equivalent fields in package.json, rather than need to re-define them in various config files.
There's was a bug in Electron causing directory listing of an ASAR subdirectory to return a listing of the ASAR root, when the path to list has a trailing slash. To fix:
dir
in recursive directory globPublished by evantahler over 8 years ago
actionhero link
to enable a plugin... we now have actionhero unlink
to remove that plugin!Will remove the linked actions, tasks, initializers, etc from a plugin in your top-level project.
Please remove the config files manually.
Remember if your plugin was installed via NPM, also be sure to remove it from your package.json or uninstall it with npm uninstall --save
connection.sendFile()
from an action). ETags are a header technology which enables browser to ask if an asset has changed by sending a SHA of the last version of the file they have, and asking the server if there is an update. This can reduce the network load of you server significantly.api.config.servers.web.enableEtag
to enable this feature, defaulting to true
.cache-contol
, Expires
, and Last-Modified
Headers if we are returning a 404 for a file requestapi.utils.hashLength
, as it was unused. User Object.keys(item).length
if you need this functionality.api.task.details
, which was broken by a previous release.Published by evantahler over 8 years ago
When sending files via HTTP in ActionHero, we make use of pipes to efficiently stream files to clients, ie: fs.createReadStream(file).pipe(response)
. We noticed that if your ActionHero sends a lot of files, it was possible to end up with a memory leak, as a growing number of connections would remain in RAM as they were still denoted as "sending data", even when they might not be.
We use the request.finish()
even to clean up the connection, and this worked well 99% of the time. However, if there was an issue with the transfer (premature client hangup, networking error, etc), that finish
event would never be called. We now also listen for a close
event, which catches these error cases.
api.chatRoom.list
api.chatRoom.list(callback)
-> callback returns error
and rooms
, which is an array of String names of your roomsExtending/modifying the functionality of Resque within ActionHero has been cumbersome and has made upgrading deployments troublesome. We now have a new section in config/tasks.js
to allow developers to replace the existing queues, scheduler, and multiWorkers with custom implementations.
For Example:
'use strict';
var NR = require('node-resque');
var pluginRunner = require('../node_modules/node-resque/lib/pluginRunner.js');
//noinspection JSUnresolvedVariable
module.exports = {
loadPriority: 590,
initialize: function(api, next){
api.customResque = {};
let customQueue = NR.queue;
customQueue.prototype.enqueueFront = function(q, func, args, callback){
var self = this;
if(arguments.length === 3 && typeof args === 'function'){
callback = args;
args = [];
}else if(arguments.length < 3){
args = [];
}
args = arrayify(args);
var job = self.jobs[func];
pluginRunner.runPlugins(self, 'before_enqueue', func, q, job, args, function(err, toRun){
if(toRun === false){
if(typeof callback === 'function'){ callback(err, toRun); }
}else{
self.connection.redis.sadd(self.connection.key('queues'), q, function(){
self.connection.redis.lpush(self.connection.key('queue', q), self.encode(q, func, args), function(){
pluginRunner.runPlugins(self, 'after_enqueue', func, q, job, args, function(){
if(typeof callback === 'function'){ callback(err, toRun); }
});
});
});
}
});
};
api.customResque.queue = customQueue;
next();
}
};
api.i18n.localize(string, options)
or api.i18n.localize([string-with-interpolation, value], options)
.evantahler/actionhero
) which will auto-build from master and contain a sample actionhero project from the latest public NPM versionFROM alpine:3.3
MAINTAINER [email protected]
RUN apk add --update nodejs
RUN npm install actionhero
RUN ./node_modules/.bin/actionhero generate
RUN npm install
CMD ["node", "./node_modules/.bin/actionhero", "start"]
EXPOSE 8080 5000
api.servers.servers
is now properly constructed via {}
rather than []
www.actionhero.com
; remove submodule dependency from the project.eslint
updated to v2.10.0should
updated to v8.3.2mocha
updated to v2.5.1node-resque
updated to v2.0.5Published by evantahler over 8 years ago
You can now map a folder like this:
get: [
{ path: '/my/special/folder', dir: __dirname + '/../my/special/folder', matchTrailingPathParts: true }
],
and access all files under the given "dir" path within the mapped path.
async
package more (clean up some gnarly old looping code)Published by evantahler over 8 years ago
api.config.servers.socket.delimiter
option to the socket server so you can change from \n
being the line delimiter.api.config.servers.socket.maxDataLength
option to the socket server to prevent buffer overflows with long data submissionsdata.connection.matchedRoute
to see the details of the route that got the web connection to this action... if in case a route was matched via the router.data.connection.matchedRoute = {
path: '/mimeTestAction/:key',
matchTrailingPathParts: false,
action: 'mimeTestAction'
}
"use strict";
from kicking in for generated actions https://github.com/evantahler/actionhero/commit/2c07d2a4728011432e8bb2febf80161d3bf416e9
Published by evantahler over 8 years ago
goodbyeMessage
for socket clientsYou can now configure the message the server sends to connections on the socket
server type with api.config.servers.socket.goodbyeMessage
. This string can be localized.
Thanks to @krishnaglick, @iSimar, @peterlandry, and @evantahler for their work keeping out documentation up to date!
ioredis
to the latest versionPublished by evantahler over 8 years ago
Fix a bug that prevented actionhero link
from working when config files were in nested directories within plugins, ie: plugin/config/server/myServer.js
let
vs var
in new action templates
Published by evantahler over 8 years ago
We now enforce javasctipt's strict mode
(via 'use strict';
) at the top of most Actionhero files. This enables easier opt-in to new ES6 features, and will help catch errors which might sneak by otherwise. You should notice no change in Actionhero's behavior. Newly generated files (Actions, Tasks, etc) will also contain the line 'use strict';
at the top of each file, but you can remove it if you don't want strict mode for some reason.
You can learn more about strict mode here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
null
HTTP HeadersYou can now set some of the default HTTP headers to null
if you don't want them returned to the client. For example, if you no longer want to send Access-Control-Allow-Origin
, you can do the following in config/servers/web.js
:
exports.production = {
servers: {
web: function(api){
return {
httpHeaders : {
'Access-Control-Allow-Origin' : null,
},
}
}
}
}