Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
astrotars committed Jun 6, 2016
0 parents commit 47ff2e2
Show file tree
Hide file tree
Showing 434 changed files with 425,202 additions and 0 deletions.
43 changes: 43 additions & 0 deletions .gitignore
@@ -0,0 +1,43 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
node_modules

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# OS
.DS_Store

env

app/public/js/bundle.js
app/public/js/app.js

# idea
.idea/
9 changes: 9 additions & 0 deletions README.md
@@ -0,0 +1,9 @@
# 7 Step - React/Redux tutorial series

This example application created by [getstream.io](https://getstream.io/?ref=github_stream_react_example) teaches you how to to build an Instagram style application with activity streams and newsfeeds.

Visit [cabin.getstream.io](http://cabin.getstream.io/) for an overview of all 7 tutorials and a live demo. If you enjoy this tutorial please star this repo.

<p align="center">
<img src="https://stream-cabin.s3.amazonaws.com/defaults/Cabin_Github@2x.png" alt="Examples of what you can build" title="What you can build"/>
</p>
Empty file added api/.gitignore
Empty file.
44 changes: 44 additions & 0 deletions api/config.js
@@ -0,0 +1,44 @@
'use strict';

/**
* Config
*/
module.exports = {
name: 'GetStream.io - React Example App',
version: '1.0.0',
env: process.env.NODE_ENV || 'DEVELOPMENT',
port: process.env.PORT || 8000,
jwt: {
secret: process.env.JWT_SECRET,
},
db: {
name: 'cabin',
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
},
mapbox: {
accessToken: process.env.MAPBOX_ACCESS_TOKEN,
},
s3: {
key: process.env.S3_KEY,
secret: process.env.S3_SECRET,
bucket: process.env.S3_BUCKET,
},
stream: {
appId: process.env.STREAM_APP_ID,
key: process.env.STREAM_KEY,
secret: process.env.STREAM_SECRET,
},
algolia: {
appId: process.env.ALGOLIA_APP_ID,
searchOnlyKey: process.env.ALGOLIA_SEARCH_ONLY_KEY,
apiKey: process.env.ALGOLIA_API_KEY,
},
keen: {
projectId: process.env.KEEN_PROJECT_ID,
writeKey: process.env.KEEN_WRITE_KEY,
readKey: process.env.KEEN_READ_KEY,
},
};
90 changes: 90 additions & 0 deletions api/index.js
@@ -0,0 +1,90 @@
'use strict';

/**
* Env Configuration
*/
require('dotenv').config({ path: 'env' });

/**
* Module Dependencies
*/
var config = require('./config'),
bunyan = require('bunyan'),
winston = require('winston'),
bunyanWinston = require('bunyan-winston-adapter'),
mysql = require('mysql'),
jwt = require('restify-jwt');

/**
* Global Dependencies
*/
global.__base = __dirname + '/';
global.config = require('./config.js');
global.restify = require('restify');

/**
* Logging
*/
global.log = new winston.Logger({
transports: [
new winston.transports.Console({
level: 'info',
timestamp: function() {
return new Date().toString();
},
json: true
})
]
});

/**
* Initialize Server
*/
global.server = restify.createServer({
name : config.name,
version : config.version,
log : bunyanWinston.createAdapter(log),
});

/**
* Middleware
*/
server.use(restify.bodyParser());
server.use(restify.acceptParser(server.acceptable));
server.use(restify.authorizationParser());
server.use(restify.queryParser({ mapParams: true }));
server.pre(require('./lib/cors')());
server.use(restify.fullResponse());
server.use(jwt({ secret: config.jwt.secret }).unless({
path: ['/users']
}));

/**
* Initialize MySQL Connection
*/
global.db = mysql.createConnection({
host : config.db.host,
user : config.db.username,
password : config.db.password,
database : config.db.name,
timezone: 'UTC'
});
db.connect();

db.query(`
SET sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
`)

/**
* Boot
*/
server.listen(config.port, function () {
require('./routes');
log.info(
'%s v%s ready to accept connections on port listening on port %s in %s environment',
server.name,
config.version,
config.port,
config.env
);
});
42 changes: 42 additions & 0 deletions api/lib/cors/index.js
@@ -0,0 +1,42 @@
'use strict';

function cors(options) {

const defaultAllowHeaders = ['Authorization', 'Content-Type'];
const defaultAllowMethods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD"];

const opts = Object.assign({}, {
allowHeaders: defaultAllowHeaders,
allowMethods: defaultAllowMethods,
allowOrigins: null,
allowCreds: true,
}, options || {});

const setHeader = (req, res, methods) => {
const origin = req.headers.origin;
const requestMethod = req.headers['access-control-request-method'];
const requestHeaders = req.headers['access-control-request-headers'];

res.once('header', () => {
if (opts.allowCreds) res.header('Access-Control-Allow-Credentials', 'true');

if (opts.allowOrigins) {
res.header('Access-Control-Allow-Origin',
(Array.isArray(opts.allowOrigins)) ? opts.allowOrigins.join(', ') : opts.allowOrigins);
} else {
res.header('Access-Control-Allow-Origin', origin);
}

res.header('Access-Control-Allow-Methods', opts.allowMethods.join(', '));
res.header('Access-Control-Allow-Headers', opts.allowHeaders.map(h => h.toUpperCase()).join(', '));
});
};

return (req, res, next) => {
setHeader(req, res);
if (req.method == 'OPTIONS') return res.send(200);
return next();
};
};

module.exports = cors;
130 changes: 130 additions & 0 deletions api/lib/stream_utils/index.js
@@ -0,0 +1,130 @@
'use strict';
var async = require('async');

function referencesFromActivities(activitiesOrNotifications) {
/*
Returns the references from a list of activities
*/
var references = {}
activitiesOrNotifications.forEach(function(item) {
var activities = (item.activities) ? item.activities : [
item
];
activities.forEach(function(activity) {
Object.keys(activity).forEach(function(key) {
if (activity[key] && activity[key].indexOf && activity[key].indexOf(':') != -1) {
var parts = activity[key].split(':');
var reference = parts[0];
var referenceId = parts[1];
if (!(reference in references)) {
references[reference] = {};
}
references[reference][
referenceId
] = 1;
}
});
});
});
return references;
};

function loadReferencedObjects(references, userId, callback) {
// TODO: subqueries are inneficient. Handle do i like and do i follow
// in 2 separate queries
var queries = [];
if (references.upload) {
let sql = `
SELECT
uploads.id AS id,
users.id AS user_id,
users.first_name AS first_name,
users.last_name AS last_name,
MD5(users.email) AS email_md5,
uploads.id AS upload_id,
uploads.filename AS filename,
uploads.hashtags AS hashtags,
uploads.caption AS caption,
uploads.location AS location,
IF((SELECT 1 AS liked FROM likes WHERE user_id = ? AND upload_id = uploads.id), true, false) AS liked
FROM uploads
LEFT JOIN users ON (uploads.user_id = users.id)
WHERE uploads.id IN (?)
`;
queries.push({
'name': 'upload',
'sql': sql
});
}
if (references.user) {
// do the same thing for users
let sql = `
SELECT
users.id AS id,
users.id AS user_id,
users.first_name AS first_name,
users.last_name AS last_name,
MD5(users.email) AS email_md5,
IF(
(
SELECT
1 AS following
FROM followers AS f
WHERE f.follower_id = users.id
AND f.user_id = ?
),
true,
false
) AS following
FROM users
WHERE users.id IN (?)
`;
queries.push({
'name': 'user',
'sql': sql
});
}
var referencedObject = {};
// run all the queries
async.eachSeries(queries, function iteratee(query, cb) {
db.query(query.sql, [userId, Object.keys(references[query.name])], function(err, results) {
if (err) {
cb(err);
}
referencedObject[query.name] = {};
results.forEach(function(result) {
referencedObject[query.name][result.id] = result;
});
cb();
});
}, function done() {
callback(referencedObject);
});
}

function enrichActivities(activitiesOrNotifications, refencedObjects) {
/*
* Enriches the activities by replacing references with the actual objects
*/
activitiesOrNotifications.forEach(function(item) {
var activities = (item.activities) ? item.activities : [item];
activities.forEach(function(activity) {
Object.keys(activity).forEach(function(key) {
if (activity[key] && activity[key].indexOf && activity[key].indexOf(':') != -1) {
var parts = activity[key].split(':');
var reference = parts[0];
var referenceId = parts[1];
if (reference in refencedObjects && refencedObjects[reference][referenceId]) {
activity[key] = refencedObjects[reference][referenceId];
}
}
});
});
});
};

module.exports = {
'referencesFromActivities': referencesFromActivities,
'loadReferencedObjects': loadReferencedObjects,
'enrichActivities': enrichActivities
};

0 comments on commit 47ff2e2

Please sign in to comment.