Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

basic API and example working

  • Loading branch information...
commit 990f5199515230ac77942e31e3550a4d04fdc54e 0 parents
@mahemoff authored
3  .gitignore
@@ -0,0 +1,3 @@
+node_modules
+.monitor
+.*.sw?
19 LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2011 by Nuvomondo Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
32 README
@@ -0,0 +1,32 @@
+An easy way to build Twitter apps with Node+Connect+Express.
+The library abstracts away the logic of connecting and disconnecting, building
+on the oauth library.
+
+It sets up /sessions/login and /sessions/logout paths, as well as a
+/sessions/debug path and, under the hood, some other paths.
+
+// MAKE SURE YOU INCLUDE COOKIE PARSER AND SESSION BEFORE THIS LIBRARY
+app.use(express.cookieParser());
+app.use(express.session({ secret:'randomness' }));
+app.use(twitter.middleware({
+ consumerKey: 'your-consumer-key',
+ consumerSecret: 'your-consumer-secret',
+ baseURL: 'http://your-app.com'
+}));
+
+app.get('/', function(req, res) {
+ if (req.session.twitter) res.send("hi " + req.session.twitter.name);
+ else res.send("<a href='log in'>/sessions/login</a>");
+});
+
+app.get('/you', function(req, res) {
+ twitter.get('http://twitter.com/account/verify_credentials.json', req,
+ function(err, data, response) {
+ res.send("Twitter says of you: "+sys.inspect(JSON.parse(data)));
+ });
+}
+
+Right now, you still have to construct the REST path to call.
+(https://dev.twitter.com/docs/api) The library may expand to support
+key high level abstractions, e.g. twitter.getTimeline(user);
+
4 example.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+./make.sh
+echo Running on http://localhost:3000 ...
+coffee examples/example.coffee
36 examples/example.coffee
@@ -0,0 +1,36 @@
+# GENERAL SETUP
+express = require 'express'
+twitter = require '../index.js'
+
+app = express.createServer()
+
+app.use express.cookieParser()
+app.use express.session { secret:'randomness' }
+app.use twitter.middleware
+ consumerKey: "3T3sx20TMn8z1uC2EXWMw" # Use your own key from Twitter's dev dashboard
+ consumerSecret: "engeqrh2yyeTdE1BkSzwEs7qozHoWjuP6lt1NDFpBBw" # ditto
+ baseURL: 'http://localhost:3000' # Your app's URL, used for Twitter callback
+ logging: true # If true, uses winston to log.
+ afterLogin: '/hello' # Page user returns to after twitter.com login
+ afterLogout: '/goodbye' # Page user returns to after twitter.com logout
+
+app.get '/', (req, res) ->
+ message = if req.session.twitter
+ then "Ahoy #{req.session.twitter.name}. <a href='/sessions/logout'>Logout</a>"
+ else 'Logged out. <a href="/sessions/login">Login Now!</a>'
+ res.send "<h3>express-twitter demo</h3><p>#{message}</p>"
+
+app.get '/hello', (req, res) ->
+ res.send """Welcome #{req.session.twitter.name}.<hr/>
+ <a href="/sessions/debug">debug</a> <a href="/you">about you</a>
+ <a href="/logout">logout</a>"""
+
+app.get '/goodbye', (req, res) ->
+ res.send 'Our paths crossed but briefly.'
+
+app.get '/you', (req, res) ->
+ twitter.get 'http://twitter.com/account/verify_credentials.json', req, (err, data, response) ->
+ user = JSON.parse(data)
+ res.send "Hello #{user.name}. Twitter says of you:<pre>#{sys.inspect(user)}</pre>"
+
+app.listen 3000
1  index.js
@@ -0,0 +1 @@
+module.exports = require('./lib');
125 lib/index.js
@@ -0,0 +1,125 @@
+/* PLEASE DON'T EDIT THIS FILE AS IT'S AUTOGENERATED.
+ * The source is index.coffee. Change that.
+ */
+
+(function() {
+ var T;
+ 'express,oauth,sys,winston'.split(',').forEach(function(lib) {
+ return eval("" + lib + " = require('" + lib + "')");
+ });
+ T = {
+ login: function(req, res) {
+ return T.consumer.getOAuthRequestToken(function(error, oauthToken, oauthTokenSecret, results) {
+ if (error) {
+ T.log.info("login error " + error);
+ return T.sendError(req, res, "Error getting OAuth request token : " + sys.inspect(error), 500);
+ } else {
+ req.session || (req.session = {});
+ req.session.oauthRequestToken = oauthToken;
+ req.session.oauthRequestTokenSecret = oauthTokenSecret;
+ return res.redirect("https://twitter.com/oauth/authorize?oauth_token=" + req.session.oauthRequestToken);
+ }
+ });
+ },
+ logout: function(req, res) {
+ T.log.info("" + req.session.twitter.name + " logged out");
+ delete req.session.twitter;
+ return res.redirect(T.options.afterLogout);
+ },
+ callback: function(req, res) {
+ return T.consumer.getOAuthAccessToken(req.session.oauthRequestToken, req.session.oauthRequestTokenSecret, req.query.oauth_verifier, function(err, oauthAccessToken, oauthAccessTokenSecret, results) {
+ if (err) {
+ T.sendError(req, res, ("Error getting OAuth access token : " + (sys.inspect(err))) + ("[" + oauthAccessToken + "] [" + oauthAccessTokenSecret + "] [" + (sys.inspect(results)) + "]"));
+ }
+ console.log("results", results, typeof results);
+ req.session.twitter = {
+ accessToken: oauthAccessToken,
+ accessTokenSecret: oauthAccessTokenSecret,
+ name: results.screen_name
+ };
+ res.redirect(T.options.afterLogin);
+ return console.log("Redirected to " + T.options.afterLogin);
+ });
+ },
+ sendError: function(req, res, err) {
+ if (err) {
+ T.log.info("Login error " + err);
+ if (process.env['NODE_ENV'] === 'development') {
+ return res.send("Login error: " + err, 500);
+ } else {
+ return res.send('<h1>Sorry, a login error occurred</h1>', 500);
+ }
+ } else {
+ return res.redirect('/');
+ }
+ },
+ debug: function(req, res) {
+ var m;
+ if (process.env['NODE_ENV'] !== 'development') {
+ return res.send('', 404);
+ }
+ m = '<p><a href="/sessions/login">Login</a> <a href="/sessions/logout">Logout</a></p><h1>Session</h1>';
+ if (req.session) {
+ m += "<details><summary>exists</summary><pre>" + (sys.inspect(req.session)) + "</pre></details>";
+ } else {
+ m = '<p>No session. Make sure you included cookieDecoder and session middleware BEFORE twitter.</p>';
+ }
+ return res.send(m);
+ },
+ emptyLogger: {
+ debug: function() {
+ return null;
+ },
+ info: function() {
+ return null;
+ }
+ },
+ middleware: function(_options) {
+ var _base, _base2;
+ T.options = _options || {};
+ (_base = T.options).afterLogin || (_base.afterLogin = '/');
+ (_base2 = T.options).afterLogout || (_base2.afterLogout = '/');
+ T.log = T.options.logging ? winston : T.emptyLogger;
+ T.consumer = new oauth.OAuth("https://twitter.com/oauth/request_token", "https://twitter.com/oauth/access_token", T.options.consumerKey, T.options.consumerSecret, "1.0A", "" + T.options.baseURL + "/sessions/callback", "HMAC-SHA1");
+ return function(req, res, next) {
+ var action;
+ if (req.url === '/sessions/login') {
+ action = T.login;
+ } else if (req.url === '/sessions/logout') {
+ action = T.logout;
+ } else if (req.url === '/sessions/debug') {
+ action = T.debug;
+ } else if (req.url.match(/^\/sessions\/callback/)) {
+ action = T.callback;
+ }
+ if (action) {
+ return action(req, res);
+ } else {
+ return next();
+ }
+ };
+ },
+ get: function(apiPath, req, callback) {
+ if (req.session.twitter == null) {
+ callback('no twitter session');
+ }
+ return T.consumer.get(apiPath, req.session.twitter.accessToken, req.session.twitter.accessTokenSecret, function(err, data, response) {
+ return callback(err, data, response);
+ });
+ },
+ post: function(apiPath, req, body, callback) {
+ if (req.session.twitter == null) {
+ callback('no twitter session');
+ }
+ T.consumer.post(apiPath, req.session.twitter.accessToken, req.session.twitter.accessTokenSecret, content);
+ return function(err, data, response) {
+ return callback(err, data, response);
+ };
+ }
+ };
+ module.exports = {
+ middleware: T.middleware,
+ get: T.get,
+ post: T.post
+ };
+}).call(this);
3  make.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+cp -f src/preamble.js lib/index.js
+coffee -p src/index.coffee >> lib/index.js
16 package.json
@@ -0,0 +1,16 @@
+{
+ "name":"twitter",
+ "main": "twitter.coffee",
+ "version": "0.0.1",
+ "homepage": "http://github.com/mahemoff/express-twitter",
+ "dependencies": {
+ "oauth": "",
+ "express": "",
+ "winston": ""
+ },
+ "keywords": ["twitter", "connect", "express", "middleware", "oauth"],
+ "repository" : {
+ "type" : "git",
+ "url" : "http://github.com/isaacs/npm.git"
+ }
+}
109 src/index.coffee
@@ -0,0 +1,109 @@
+'express,oauth,sys,winston'
+ # .split(',').forEach (lib) -> eval "console.log('#{lib}');"
+ .split(',').forEach (lib) -> eval "#{lib} = require('#{lib}')"
+
+T =
+
+ ##############################################################################
+ # ACTIONS
+ ##############################################################################
+
+ login: (req, res) ->
+ T.consumer.getOAuthRequestToken (error, oauthToken, oauthTokenSecret, results) ->
+ if error
+ T.log.info "login error #{error}"
+ return T.sendError req, res, "Error getting OAuth request token : " + sys.inspect(error), 500
+ else
+ req.session ||= {}
+ req.session.oauthRequestToken = oauthToken
+ req.session.oauthRequestTokenSecret = oauthTokenSecret
+ return res.redirect "https://twitter.com/oauth/authorize?oauth_token=#{req.session.oauthRequestToken}"
+
+ logout: (req, res) ->
+ T.log.info "#{req.session.twitter.name} logged out"
+ delete req.session.twitter
+ res.redirect T.options.afterLogout
+
+ callback: (req,res) ->
+ T.consumer.getOAuthAccessToken req.session.oauthRequestToken,
+ req.session.oauthRequestTokenSecret, req.query.oauth_verifier,
+ (err, oauthAccessToken, oauthAccessTokenSecret, results) ->
+
+ if err
+ T.sendError req, res, "Error getting OAuth access token : #{sys.inspect(err)}" +
+ "[#{oauthAccessToken}] [#{oauthAccessTokenSecret}] [#{sys.inspect(results)}]"
+
+ console.log "results", results, typeof(results)
+ req.session.twitter =
+ accessToken: oauthAccessToken
+ accessTokenSecret: oauthAccessTokenSecret
+ name: results.screen_name
+ res.redirect T.options.afterLogin
+ console.log "Redirected to #{T.options.afterLogin}"
+
+ ##############################################################################
+ # UTILS/HELPERS
+ ##############################################################################
+
+ sendError: (req,res,err) ->
+ if err
+ T.log.info "Login error #{err}"
+ if process.env['NODE_ENV']=='development'
+ res.send "Login error: #{err}", 500
+ else
+ res.send '<h1>Sorry, a login error occurred</h1>', 500
+ else
+ res.redirect '/' # todo
+
+ debug: (req,res) ->
+ return res.send('',404) unless process.env['NODE_ENV']=='development'
+ m='<p><a href="/sessions/login">Login</a> <a href="/sessions/logout">Logout</a></p><h1>Session</h1>'
+ if req.session
+ m+="<details><summary>exists</summary><pre>#{sys.inspect(req.session)}</pre></details>"
+ else
+ m='<p>No session. Make sure you included cookieDecoder and session middleware BEFORE twitter.</p>'
+ res.send m
+
+ emptyLogger:
+ debug: () -> null
+ info: () -> null
+
+ ##############################################################################
+ # PUBLIC INTERFACE
+ ##############################################################################
+
+ middleware: (_options) ->
+
+ T.options = _options || {}
+ T.options.afterLogin ||= '/'
+ T.options.afterLogout ||= '/'
+ T.log = if T.options.logging then winston else T.emptyLogger
+
+ T.consumer = new oauth.OAuth(
+ "https://twitter.com/oauth/request_token", "https://twitter.com/oauth/access_token",
+ T.options.consumerKey, T.options.consumerSecret
+ "1.0A", "#{T.options.baseURL}/sessions/callback", "HMAC-SHA1")
+
+ return (req, res, next) ->
+ if req.url=='/sessions/login' then action=T.login
+ else if req.url=='/sessions/logout' then action=T.logout
+ else if req.url=='/sessions/debug' then action=T.debug
+ else if req.url.match(/^\/sessions\/callback/) then action=T.callback
+ if action then action req,res else next()
+
+ get: (apiPath, req, callback) ->
+ callback 'no twitter session' unless req.session.twitter?
+ T.consumer.get apiPath, req.session.twitter.accessToken, req.session.twitter.accessTokenSecret,
+ (err, data, response) ->
+ callback err, data, response
+
+ post: (apiPath, req, body, callback) ->
+ callback 'no twitter session' unless req.session.twitter?
+ T.consumer.post apiPath, req.session.twitter.accessToken, req.session.twitter.accessTokenSecret, content
+ (err, data, response) ->
+ callback err, data, response
+
+module.exports =
+ middleware: T.middleware
+ get: T.get
+ post: T.post
4 src/preamble.js
@@ -0,0 +1,4 @@
+/* PLEASE DON'T EDIT THIS FILE AS IT'S AUTOGENERATED.
+ * The source is index.coffee. Change that.
+ */
+
Please sign in to comment.
Something went wrong with that request. Please try again.