Skip to content

Commit

Permalink
feat: initial release of jsonbin.org
Browse files Browse the repository at this point in the history
  • Loading branch information
remy committed Jan 4, 2017
0 parents commit 8b614d5
Show file tree
Hide file tree
Showing 31 changed files with 2,156 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env
.*.env
node_modules
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!.env
24 changes: 24 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const dotenv = require('dotenv');

let env = '.env';

if (process.env.NODE_ENV !== 'production') {
env = `.${process.env.NODE_ENV}${env}`;
}

console.log(`Loading ${env}`);

if (!process.env.PORT) {
process.env.PORT = 3000;
}

if (!process.env.SESSION_SECRET) {
process.env.SESSION_SECRET = 'store.fklwejlfjwe';
}

dotenv.config({
silent: true,
path: __dirname + '/../' + env,
});

module.exports = process.env;
16 changes: 16 additions & 0 deletions lib/db/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const mongoose = require('mongoose');
mongoose.Promise = global.Promise; // http://mongoosejs.com/docs/promises.html

mongoose.connect(process.env.MONGO_URL);
const connection = mongoose.connection;

connection.on('error', (...rest) => {
console.log(arguments[0].stack);
console.error.apply(console, rest);
});

connection.once('open', () => {
console.log('db connected');
});

module.exports = mongoose;
65 changes: 65 additions & 0 deletions lib/db/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const mongoose = require('mongoose');
const uuid = require('uuid');

const schema = mongoose.Schema({
username: {
type: String,
required: true,
index: {
unique: true,
},
},
publicId: {
type: String,
default: uuid.v4,
required: true,
index: {
unique: true,
},
},
apikey: {
type: String,
default: uuid.v4,
required: true,
index: {
unique: true,
},
},
email: {
type: String,
lowercase: true,
trim: true,
index: {
unique: true,
},
},
password: String,
store: {},
public: [],
}, { strict: false });


schema.statics.findOrCreate = ({ email = null } = {}, data) => {
return User.findOne({ email }).then(user => {
if (user) {
return user;
}
console.log('making new');
data.email = email;
data.apikey = uuid.v4();
data.store = {
gettingStarted: `check out the *help*, and try 'curl ${process.env.HOST} -H "authorization: token ${data.apikey}"'`,
urls: ['help', 'me', 'logout'].map(_ => `${process.env.HOST}/_/${_}`),
};

// FIXME allow users to change their username
data.username = data.githubProfile.login;

return new User(data).save();
}).catch(e => {
console.log('failed', e);
throw e;
});
};

const User = module.exports = mongoose.model('user', schema);
250 changes: 250 additions & 0 deletions lib/hbs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
const crypto = require('crypto');
const hbs = require('express-hbs');
const moment = require('moment');

module.exports = hbs;

const md5 = (data) => crypto.createHash('md5').update(data).digest('hex');

hbs.registerHelper('options', function (selected, ...data) {
const opts = data.pop();
let options = '';
for (const key of data) {
if (selected === key) {
options += `<option selected value="${key}">${key}</option>`;
} else {
options += `<option value="${key}">${key}</option>`;
}
}
return new hbs.SafeString(options);
});


hbs.registerHelper('if_empty', function (data, options) {
const keys = Object.keys(data);
return keys.length === 0 ? options.fn(this) : options.inverse(this);
});

hbs.registerHelper('each_sorted', (context, options) => {
let data = {};

if (options.data) {
data = hbs.handlebars.createFrame(options.data);
}

return Object.keys(context).sort((a, b) => {
return countries[a.toUpperCase()].name < countries[b.toUpperCase()].name ? -1 : 1;
}).map((_, i) => {
if (data) {
data.index = i;
}

data.key = _;

return options.fn(context[_], { data });
}).join('');
});

hbs.registerHelper('avatar', (account, ...rest) => {
const opts = rest.pop();
let size = 32;
if (rest.length) {
size = rest.shift();
}

let img = null;

if (account.logo || account.accountData.email) {
const url = account.logo ||
`https://www.gravatar.com/avatar/${md5(account.accountData.email)}?s=${size * 2}`;
img = `<img width="${size}" class="avatar circle" src="${url}">`;
}

if (!img) {
img = '<img width="${size}" src="/img/tmp-avatar.svg" class="circle green tmp-avatar">';
}

return new hbs.SafeString(img);
});

hbs.registerHelper('gravatar', (email, ...rest) => {
const opts = rest.pop();
let size = 64;
if (rest.length) {
size = rest.shift();
}
return 'https://www.gravatar.com/avatar/' + md5(email) + '?s='+ size;
});

hbs.registerHelper('tomorrow', f => moment().utc().add(1, 'day').format(f));
hbs.registerHelper('upper', s => s.toUpperCase());
hbs.registerHelper('lower', s => s.toLowerCase());

hbs.registerHelper('fixed', (value, ...rest) => {
const opts = rest.pop();
const code = (rest.shift() || 'gbp').toUpperCase();
const ccy = getSymbolFromCurrency(code);
return ccy + (value / 100).toLocaleString('en-IN', {
minimumFractionDigits: currencies[code].decimals,
maximumFractionDigits: currencies[code].decimals,
});
});

hbs.registerHelper('moment-format', (date, format) => {
if (typeof format !== 'string') {
format = date;
date = new Date();
}
return moment.utc(date).format(format);
});

hbs.registerHelper('urlFormat', (url, ...args) => {
const opts = args.pop();
if (!url.includes('?')) {
url += '?';
} else {
url += '&';
}
return url + args.join('&');
});

// removes the "in" text, just number and unit
hbs.registerHelper('momentFromNow', from => {
if (from > Date.now()) { // future?!
return 'no days';
}
return moment.utc(from).fromNow();
});

hbs.registerHelper('dump', data => JSON.stringify(data, null, 2));

hbs.registerHelper('sort', (data, prop, options) => {
return options.fn(data.sort((a, b) => {
return a[prop] < b[prop] ? -1 : 1;
}));
});

hbs.registerHelper('firstNonFalse', (data, options) => {
return options.fn(data.filter(Boolean).shift());
});

hbs.registerHelper('if_eq', (a, b, opts) => {
return (a === b) ? opts.fn(this) : opts.inverse(this);
});

hbs.registerHelper('if_all', function () { // important: not an arrow fn
const args = [].slice.call(arguments);
const opts = args.pop();

return args.every(v => !!v) ? opts.fn(this) : opts.inverse(this);
});

hbs.registerHelper('if_any', (...args) => { // important: not an arrow fn
const opts = args.pop();

return args.some(v => !!v) ? opts.fn(this) : opts.inverse(this);
});

hbs.registerHelper('unless_eq', (a, b, opts) => {
return (a !== b) ? opts.fn(this) : opts.inverse(this);
});

hbs.registerHelper('ifCond', (v1, operator, v2, options) => {
switch (operator) {
case '==': {
return (v1 == v2) ? options.fn(this) // jshint ignore:line
: options.inverse(this);
}
case '===': {
return (v1 === v2) ? options.fn(this) : options.inverse(this);
}
case '<': {
return (v1 < v2) ? options.fn(this) : options.inverse(this);
}
case '<=': {
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
}
case '>': {
return (v1 > v2) ? options.fn(this) : options.inverse(this);
}
case '>=': {
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
}
case '&&': {
return (v1 && v2) ? options.fn(this) : options.inverse(this);
}
case '||': {
return (v1 || v2) ? options.fn(this) : options.inverse(this);
}
default: {
return options.inverse(this);
}
}
});

/**
* {{#feature user 'alpha'}}
* <!-- here be the new stuff -->
* {{else}}
* <!--- unentitled users get this stuff -->
* {{/feature}}
*/
// hbs.registerHelper('feature', function (user, flag, opts) {
// // the convention is that the feature receives a request object
// // that will contain the user as a property, so I'm fleshing it out here.
// return (features(flag, { user: user })) ? opts.fn(this) : opts.inverse(this);
// });

hbs.registerHelper({
eq: function (v1, v2) {
return v1 === v2;
},
ne: function (v1, v2) {
return v1 !== v2;
},
lt: function (v1, v2) {
return v1 < v2;
},
gt: function (v1, v2) {
return v1 > v2;
},
lte: function (v1, v2) {
return v1 <= v2;
},
gte: function (v1, v2) {
return v1 >= v2;
},
and: function (v1, v2) {
return v1 && v2;
},
or: function (v1, v2) {
return v1 || v2;
},
not: function (v) {
return !v;
},
});

hbs.registerHelper('encode', encodeURIComponent);

hbs.registerHelper('json', JSON.stringify.bind(JSON));

hbs.registerHelper('math', function (lvalue, operator, rvalue) {
lvalue = parseFloat(lvalue);
rvalue = parseFloat(rvalue);

return {
'+': lvalue + rvalue,
'-': lvalue - rvalue,
'*': lvalue * rvalue,
'/': lvalue / rvalue,
'%': lvalue % rvalue,
}[operator];
});

// usage: {{pluralize collection.length 'quiz' 'quizzes'}}
hbs.registerHelper('pluralize', function (number, single, plural) {
return (number === 1) ? single : plural;
});

hbs.registerHelper('lower', text => text.toLowerCase());
Loading

0 comments on commit 8b614d5

Please sign in to comment.