-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial release of jsonbin.org
- Loading branch information
0 parents
commit 8b614d5
Showing
31 changed files
with
2,156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.env | ||
.*.env | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
!.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); |
Oops, something went wrong.