Skip to content

Instantly share code, notes, and snippets.

@sekoyo
Last active December 13, 2020 00:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sekoyo/8042974 to your computer and use it in GitHub Desktop.
Save sekoyo/8042974 to your computer and use it in GitHub Desktop.
Connect to Phabricator with node.js
var Connect = function() {
this.fs = require('fs');
this.crypto = require('crypto');
this.http = require('http');
this.url = require('url');
this.q = require('q');
this.initialize();
};
Connect.prototype = {
apiPath: 'conduit.connect',
_isConnected: false,
initialize: function() {
this.attributes = {};
this.parseArcRC();
},
isConnected: function() {
// See if we connected 14.5minutes ago (connection expires every 15mins).
var inTime = (this.timeStamp + (14.5 * 60) > (new Date).getTime() / 1000);
return this._isConnected && inTime;
},
getArcRCContents: function() {
return this.fs.readFileSync(process.env.HOME + '/.arcrc', {encoding:'utf8'});
},
parseArcRC: function() {
this.hosts = JSON.parse(this.getArcRCContents()).hosts;
if (!this.hosts) {
throw new Error('No hosts found in ~/.arcrc');
}
},
buildAuthSig: function(authToken, cert) {
var shasum = this.crypto.createHash('sha1');
return shasum.update(authToken + cert).digest('hex');
},
to: function(hostOrIndex) {
var deferred = this.q.defer();
// See if we are already connected.
if (this.isConnected()) {
deferred.resolve(self.attributes);
return deferred.promise;
}
// Normalize host key.
var key = hostOrIndex,
self = this;
if (typeof hostOrIndex === 'number') {
key = Object.keys(this.hosts)[hostOrIndex];
}
this.timeStamp = (new Date).getTime() / 1000;
this._isConnected = false;
// Post connection request.
var hostEntry = this.hosts[key],
host = this.url.parse(key),
authToken = this.timeStamp,
authSignature = this.buildAuthSig(this.timeStamp, hostEntry.cert),
postParams = {
user: hostEntry.user,
client: 'node-phabricator',
host: key,
authToken: authToken,
authSignature: authSignature
},
formData = 'params='+JSON.stringify(postParams)+'&output=json',
postOptions = {
host: host.host,
port: 80,
path: '/api/' + this.apiPath,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(formData)
}
},
postReq = this.http.request(postOptions, function(res) {
self._isConnected = res.statusCode === 200;
res.setEncoding('utf8');
res.on('data', function (data) {
if (self._isConnected) {
var jsonData = JSON.parse(data);
self.attributes = {
connectionID: jsonData.result.connectionID,
sessionKey: jsonData.result.sessionKey,
userPHID: jsonData.result.userPHID
};
deferred.resolve(self.attributes);
} else {
self.attributes = {};
deferred.reject(new Error('Could not connect to host.' + data));
}
});
});
postReq.write(formData);
postReq.end();
return deferred.promise;
},
toFirst: function() {
return this.to(0);
}
};
module.exports = Connect;
@sekoyo
Copy link
Author

sekoyo commented Dec 20, 2013

Example usage:

var connect = new Connect();

connect.toFirst().then(function(attrs) {
    console.log('connected:', attrs);
});

@sekoyo
Copy link
Author

sekoyo commented Dec 20, 2013

Note: formData doesn't use something like qs.stringify on the whole object as Phabricator strangely expects only the stuff in params to be in quotes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment