Compare commits
5 Commits
master
...
production
Author | SHA1 | Date |
---|---|---|
|
32df3370e2 | 8 years ago |
|
d81195856a | 8 years ago |
|
6ed427658e | 8 years ago |
|
08eddc7e80 | 8 years ago |
|
d040dedc6e | 8 years ago |
36 changed files with 178 additions and 2727 deletions
@ -1,8 +0,0 @@ |
|||||||
Dockerfile |
|
||||||
.git |
|
||||||
npm-debug.log |
|
||||||
node_modules |
|
||||||
*.swp |
|
||||||
*.swo |
|
||||||
data |
|
||||||
*.DS_Store |
|
@ -1,2 +0,0 @@ |
|||||||
**/*.min.js |
|
||||||
config.js |
|
@ -1,25 +0,0 @@ |
|||||||
{ |
|
||||||
"env": { |
|
||||||
"es6": true, |
|
||||||
"node": true |
|
||||||
}, |
|
||||||
"extends": "eslint:recommended", |
|
||||||
"rules": { |
|
||||||
"indent": [ |
|
||||||
"error", |
|
||||||
2 |
|
||||||
], |
|
||||||
"linebreak-style": [ |
|
||||||
"error", |
|
||||||
"unix" |
|
||||||
], |
|
||||||
"quotes": [ |
|
||||||
"error", |
|
||||||
"single" |
|
||||||
], |
|
||||||
"semi": [ |
|
||||||
"error", |
|
||||||
"always" |
|
||||||
] |
|
||||||
} |
|
||||||
} |
|
@ -1 +0,0 @@ |
|||||||
* @toptal/site-acquisition-eng |
|
@ -1,30 +0,0 @@ |
|||||||
name: Close inactive issues and PRs |
|
||||||
on: |
|
||||||
workflow_dispatch: |
|
||||||
schedule: |
|
||||||
- cron: "30 1 * * *" |
|
||||||
|
|
||||||
jobs: |
|
||||||
close-stale: |
|
||||||
runs-on: ubuntu-latest |
|
||||||
permissions: |
|
||||||
issues: write |
|
||||||
pull-requests: write |
|
||||||
steps: |
|
||||||
- uses: actions/stale@v3 |
|
||||||
with: |
|
||||||
days-before-stale: 30 |
|
||||||
days-before-close: 14 |
|
||||||
stale-issue-label: "stale" |
|
||||||
stale-pr-label: "stale" |
|
||||||
|
|
||||||
exempt-issue-labels: backlog,triage,nostale |
|
||||||
exempt-pr-labels: backlog,triage,nostale |
|
||||||
|
|
||||||
stale-pr-message: "This PR is stale because it has been open for 30 days with no activity." |
|
||||||
close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale." |
|
||||||
|
|
||||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." |
|
||||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." |
|
||||||
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }} |
|
@ -1,68 +0,0 @@ |
|||||||
FROM node:14.8.0-stretch |
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/app && \ |
|
||||||
chown node:node /usr/src/app |
|
||||||
|
|
||||||
USER node:node |
|
||||||
|
|
||||||
WORKDIR /usr/src/app |
|
||||||
|
|
||||||
COPY --chown=node:node . . |
|
||||||
|
|
||||||
RUN npm install && \ |
|
||||||
npm install redis@0.8.1 && \ |
|
||||||
npm install pg@4.1.1 && \ |
|
||||||
npm install memcached@2.2.2 && \ |
|
||||||
npm install aws-sdk@2.738.0 && \ |
|
||||||
npm install rethinkdbdash@2.3.31 |
|
||||||
|
|
||||||
ENV STORAGE_TYPE=memcached \ |
|
||||||
STORAGE_HOST=127.0.0.1 \ |
|
||||||
STORAGE_PORT=11211\ |
|
||||||
STORAGE_EXPIRE_SECONDS=2592000\ |
|
||||||
STORAGE_DB=2 \ |
|
||||||
STORAGE_AWS_BUCKET= \ |
|
||||||
STORAGE_AWS_REGION= \ |
|
||||||
STORAGE_USENAME= \ |
|
||||||
STORAGE_PASSWORD= \ |
|
||||||
STORAGE_FILEPATH= |
|
||||||
|
|
||||||
ENV LOGGING_LEVEL=verbose \ |
|
||||||
LOGGING_TYPE=Console \ |
|
||||||
LOGGING_COLORIZE=true |
|
||||||
|
|
||||||
ENV HOST=0.0.0.0\ |
|
||||||
PORT=7777\ |
|
||||||
KEY_LENGTH=10\ |
|
||||||
MAX_LENGTH=400000\ |
|
||||||
STATIC_MAX_AGE=86400\ |
|
||||||
RECOMPRESS_STATIC_ASSETS=true |
|
||||||
|
|
||||||
ENV KEYGENERATOR_TYPE=phonetic \ |
|
||||||
KEYGENERATOR_KEYSPACE= |
|
||||||
|
|
||||||
ENV RATELIMITS_NORMAL_TOTAL_REQUESTS=500\ |
|
||||||
RATELIMITS_NORMAL_EVERY_MILLISECONDS=60000 \ |
|
||||||
RATELIMITS_WHITELIST_TOTAL_REQUESTS= \ |
|
||||||
RATELIMITS_WHITELIST_EVERY_MILLISECONDS= \ |
|
||||||
# comma separated list for the whitelisted \ |
|
||||||
RATELIMITS_WHITELIST=example1.whitelist,example2.whitelist \ |
|
||||||
\ |
|
||||||
RATELIMITS_BLACKLIST_TOTAL_REQUESTS= \ |
|
||||||
RATELIMITS_BLACKLIST_EVERY_MILLISECONDS= \ |
|
||||||
# comma separated list for the blacklisted \ |
|
||||||
RATELIMITS_BLACKLIST=example1.blacklist,example2.blacklist |
|
||||||
ENV DOCUMENTS=about=./about.md |
|
||||||
|
|
||||||
EXPOSE ${PORT} |
|
||||||
STOPSIGNAL SIGINT |
|
||||||
ENTRYPOINT [ "bash", "docker-entrypoint.sh" ] |
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s \ |
|
||||||
--retries=3 CMD [ "sh", "-c", "echo -n 'curl localhost:7777... '; \ |
|
||||||
(\ |
|
||||||
curl -sf localhost:7777 > /dev/null\ |
|
||||||
) && echo OK || (\ |
|
||||||
echo Fail && exit 2\ |
|
||||||
)"] |
|
||||||
CMD ["npm", "start"] |
|
@ -1,12 +0,0 @@ |
|||||||
version: '3.0' |
|
||||||
services: |
|
||||||
haste-server: |
|
||||||
build: . |
|
||||||
environment: |
|
||||||
- STORAGE_TYPE=memcached |
|
||||||
- STORAGE_HOST=memcached |
|
||||||
- STORAGE_PORT=11211 |
|
||||||
ports: |
|
||||||
- 7777:7777 |
|
||||||
memcached: |
|
||||||
image: memcached:latest |
|
@ -1,108 +0,0 @@ |
|||||||
const { |
|
||||||
HOST, |
|
||||||
PORT, |
|
||||||
KEY_LENGTH, |
|
||||||
MAX_LENGTH, |
|
||||||
STATIC_MAX_AGE, |
|
||||||
RECOMPRESS_STATIC_ASSETS, |
|
||||||
STORAGE_TYPE, |
|
||||||
STORAGE_HOST, |
|
||||||
STORAGE_PORT, |
|
||||||
STORAGE_EXPIRE_SECONDS, |
|
||||||
STORAGE_DB, |
|
||||||
STORAGE_AWS_BUCKET, |
|
||||||
STORAGE_AWS_REGION, |
|
||||||
STORAGE_PASSWORD, |
|
||||||
STORAGE_USERNAME, |
|
||||||
STORAGE_FILEPATH, |
|
||||||
LOGGING_LEVEL, |
|
||||||
LOGGING_TYPE, |
|
||||||
LOGGING_COLORIZE, |
|
||||||
KEYGENERATOR_TYPE, |
|
||||||
KEY_GENERATOR_KEYSPACE, |
|
||||||
RATE_LIMITS_NORMAL_TOTAL_REQUESTS, |
|
||||||
RATE_LIMITS_NORMAL_EVERY_MILLISECONDS, |
|
||||||
RATE_LIMITS_WHITELIST_TOTAL_REQUESTS, |
|
||||||
RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS, |
|
||||||
RATE_LIMITS_WHITELIST, |
|
||||||
RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS, |
|
||||||
RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS, |
|
||||||
RATE_LIMITS_BLACKLIST, |
|
||||||
DOCUMENTS, |
|
||||||
} = process.env; |
|
||||||
|
|
||||||
const config = { |
|
||||||
host: HOST, |
|
||||||
port: Number(PORT), |
|
||||||
|
|
||||||
keyLength: Number(KEY_LENGTH), |
|
||||||
|
|
||||||
maxLength: Number(MAX_LENGTH), |
|
||||||
|
|
||||||
staticMaxAge: Number(STATIC_MAX_AGE), |
|
||||||
|
|
||||||
recompressStaticAssets: RECOMPRESS_STATIC_ASSETS, |
|
||||||
|
|
||||||
logging: [ |
|
||||||
{ |
|
||||||
level: LOGGING_LEVEL, |
|
||||||
type: LOGGING_TYPE, |
|
||||||
colorize: LOGGING_COLORIZE, |
|
||||||
}, |
|
||||||
], |
|
||||||
|
|
||||||
keyGenerator: { |
|
||||||
type: KEYGENERATOR_TYPE, |
|
||||||
keyspace: KEY_GENERATOR_KEYSPACE, |
|
||||||
}, |
|
||||||
|
|
||||||
rateLimits: { |
|
||||||
whitelist: RATE_LIMITS_WHITELIST ? RATE_LIMITS_WHITELIST.split(",") : [], |
|
||||||
blacklist: RATE_LIMITS_BLACKLIST ? RATE_LIMITS_BLACKLIST.split(",") : [], |
|
||||||
categories: { |
|
||||||
normal: { |
|
||||||
totalRequests: RATE_LIMITS_NORMAL_TOTAL_REQUESTS, |
|
||||||
every: RATE_LIMITS_NORMAL_EVERY_MILLISECONDS, |
|
||||||
}, |
|
||||||
whitelist: |
|
||||||
RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS || |
|
||||||
RATE_LIMITS_WHITELIST_TOTAL_REQUESTS |
|
||||||
? { |
|
||||||
totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS, |
|
||||||
every: RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS, |
|
||||||
} |
|
||||||
: null, |
|
||||||
blacklist: |
|
||||||
RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS || |
|
||||||
RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS |
|
||||||
? { |
|
||||||
totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS, |
|
||||||
every: RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS, |
|
||||||
} |
|
||||||
: null, |
|
||||||
}, |
|
||||||
}, |
|
||||||
|
|
||||||
storage: { |
|
||||||
type: STORAGE_TYPE, |
|
||||||
host: STORAGE_HOST, |
|
||||||
port: Number(STORAGE_PORT), |
|
||||||
expire: Number(STORAGE_EXPIRE_SECONDS), |
|
||||||
bucket: STORAGE_AWS_BUCKET, |
|
||||||
region: STORAGE_AWS_REGION, |
|
||||||
connectionUrl: `postgres://${STORAGE_USERNAME}:${STORAGE_PASSWORD}@${STORAGE_HOST}:${STORAGE_PORT}/${STORAGE_DB}`, |
|
||||||
db: STORAGE_DB, |
|
||||||
user: STORAGE_USERNAME, |
|
||||||
password: STORAGE_PASSWORD, |
|
||||||
path: STORAGE_FILEPATH, |
|
||||||
}, |
|
||||||
|
|
||||||
documents: DOCUMENTS |
|
||||||
? DOCUMENTS.split(",").reduce((acc, item) => { |
|
||||||
const keyAndValueArray = item.replace(/\s/g, "").split("="); |
|
||||||
return { ...acc, [keyAndValueArray[0]]: keyAndValueArray[1] }; |
|
||||||
}, {}) |
|
||||||
: null, |
|
||||||
}; |
|
||||||
|
|
||||||
console.log(JSON.stringify(config)); |
|
@ -1,9 +0,0 @@ |
|||||||
#!/bin/bash |
|
||||||
|
|
||||||
# We use this file to translate environmental variables to .env files used by the application |
|
||||||
|
|
||||||
set -e |
|
||||||
|
|
||||||
node ./docker-entrypoint.js > ./config.js |
|
||||||
|
|
||||||
exec "$@" |
|
@ -1,56 +0,0 @@ |
|||||||
/*global require,module,process*/ |
|
||||||
|
|
||||||
var AWS = require('aws-sdk'); |
|
||||||
var winston = require('winston'); |
|
||||||
|
|
||||||
var AmazonS3DocumentStore = function(options) { |
|
||||||
this.expire = options.expire; |
|
||||||
this.bucket = options.bucket; |
|
||||||
this.client = new AWS.S3({region: options.region}); |
|
||||||
}; |
|
||||||
|
|
||||||
AmazonS3DocumentStore.prototype.get = function(key, callback, skipExpire) { |
|
||||||
var _this = this; |
|
||||||
|
|
||||||
var req = { |
|
||||||
Bucket: _this.bucket, |
|
||||||
Key: key |
|
||||||
}; |
|
||||||
|
|
||||||
_this.client.getObject(req, function(err, data) { |
|
||||||
if(err) { |
|
||||||
callback(false); |
|
||||||
} |
|
||||||
else { |
|
||||||
callback(data.Body.toString('utf-8')); |
|
||||||
if (_this.expire && !skipExpire) { |
|
||||||
winston.warn('amazon s3 store cannot set expirations on keys'); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
AmazonS3DocumentStore.prototype.set = function(key, data, callback, skipExpire) { |
|
||||||
var _this = this; |
|
||||||
|
|
||||||
var req = { |
|
||||||
Bucket: _this.bucket, |
|
||||||
Key: key, |
|
||||||
Body: data, |
|
||||||
ContentType: 'text/plain' |
|
||||||
}; |
|
||||||
|
|
||||||
_this.client.putObject(req, function(err, data) { |
|
||||||
if (err) { |
|
||||||
callback(false); |
|
||||||
} |
|
||||||
else { |
|
||||||
callback(true); |
|
||||||
if (_this.expire && !skipExpire) { |
|
||||||
winston.warn('amazon s3 store cannot set expirations on keys'); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = AmazonS3DocumentStore; |
|
@ -1,89 +0,0 @@ |
|||||||
/*global require,module,process*/ |
|
||||||
|
|
||||||
const Datastore = require('@google-cloud/datastore'); |
|
||||||
const winston = require('winston'); |
|
||||||
|
|
||||||
class GoogleDatastoreDocumentStore { |
|
||||||
|
|
||||||
// Create a new store with options
|
|
||||||
constructor(options) { |
|
||||||
this.kind = "Haste"; |
|
||||||
this.expire = options.expire; |
|
||||||
this.datastore = new Datastore(); |
|
||||||
} |
|
||||||
|
|
||||||
// Save file in a key
|
|
||||||
set(key, data, callback, skipExpire) { |
|
||||||
var expireTime = (skipExpire || this.expire === undefined) ? null : new Date(Date.now() + this.expire * 1000); |
|
||||||
|
|
||||||
var taskKey = this.datastore.key([this.kind, key]) |
|
||||||
var task = { |
|
||||||
key: taskKey, |
|
||||||
data: [ |
|
||||||
{ |
|
||||||
name: 'value', |
|
||||||
value: data, |
|
||||||
excludeFromIndexes: true |
|
||||||
}, |
|
||||||
{ |
|
||||||
name: 'expiration', |
|
||||||
value: expireTime |
|
||||||
} |
|
||||||
] |
|
||||||
}; |
|
||||||
|
|
||||||
this.datastore.insert(task).then(() => { |
|
||||||
callback(true); |
|
||||||
}) |
|
||||||
.catch(err => { |
|
||||||
callback(false); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
// Get a file from a key
|
|
||||||
get(key, callback, skipExpire) { |
|
||||||
var taskKey = this.datastore.key([this.kind, key]) |
|
||||||
|
|
||||||
this.datastore.get(taskKey).then((entity) => { |
|
||||||
if (skipExpire || entity[0]["expiration"] == null) { |
|
||||||
callback(entity[0]["value"]); |
|
||||||
} |
|
||||||
else { |
|
||||||
// check for expiry
|
|
||||||
if (entity[0]["expiration"] < new Date()) { |
|
||||||
winston.info("document expired", {key: key, expiration: entity[0]["expiration"], check: new Date(null)}); |
|
||||||
callback(false); |
|
||||||
} |
|
||||||
else { |
|
||||||
// update expiry
|
|
||||||
var task = { |
|
||||||
key: taskKey, |
|
||||||
data: [ |
|
||||||
{ |
|
||||||
name: 'value', |
|
||||||
value: entity[0]["value"], |
|
||||||
excludeFromIndexes: true |
|
||||||
}, |
|
||||||
{ |
|
||||||
name: 'expiration', |
|
||||||
value: new Date(Date.now() + this.expire * 1000) |
|
||||||
} |
|
||||||
] |
|
||||||
}; |
|
||||||
this.datastore.update(task).then(() => { |
|
||||||
}) |
|
||||||
.catch(err => { |
|
||||||
winston.error("failed to update expiration", {error: err}); |
|
||||||
}); |
|
||||||
callback(entity[0]["value"]); |
|
||||||
} |
|
||||||
} |
|
||||||
}) |
|
||||||
.catch(err => { |
|
||||||
winston.error("Error retrieving value from Google Datastore", {error: err}); |
|
||||||
callback(false); |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = GoogleDatastoreDocumentStore; |
|
@ -1,54 +1,45 @@ |
|||||||
const memcached = require('memcached'); |
var memcached = require('memcache'); |
||||||
const winston = require('winston'); |
var winston = require('winston'); |
||||||
|
|
||||||
class MemcachedDocumentStore { |
// Create a new store with options
|
||||||
|
var MemcachedDocumentStore = function(options) { |
||||||
// Create a new store with options
|
this.expire = options.expire; |
||||||
constructor(options) { |
if (!MemcachedDocumentStore.client) { |
||||||
this.expire = options.expire; |
MemcachedDocumentStore.connect(options); |
||||||
|
|
||||||
const host = options.host || '127.0.0.1'; |
|
||||||
const port = options.port || 11211; |
|
||||||
const url = `${host}:${port}`; |
|
||||||
this.connect(url); |
|
||||||
} |
|
||||||
|
|
||||||
// Create a connection
|
|
||||||
connect(url) { |
|
||||||
this.client = new memcached(url); |
|
||||||
|
|
||||||
winston.info(`connecting to memcached on ${url}`); |
|
||||||
|
|
||||||
this.client.on('failure', function(error) { |
|
||||||
winston.info('error connecting to memcached', {error}); |
|
||||||
}); |
|
||||||
} |
} |
||||||
|
}; |
||||||
// Save file in a key
|
|
||||||
set(key, data, callback, skipExpire) { |
// Create a connection
|
||||||
this.client.set(key, data, skipExpire ? 0 : this.expire || 0, (error) => { |
MemcachedDocumentStore.connect = function(options) { |
||||||
callback(!error); |
var host = options.host || '127.0.0.1'; |
||||||
}); |
var port = options.port || 11211; |
||||||
} |
this.client = new memcached.Client(port, host); |
||||||
|
this.client.connect(); |
||||||
// Get a file from a key
|
this.client.on('connect', function() { |
||||||
get(key, callback, skipExpire) { |
winston.info('connected to memcached on ' + host + ':' + port); |
||||||
this.client.get(key, (error, data) => { |
}); |
||||||
const value = error ? false : data; |
this.client.on('error', function(e) { |
||||||
|
winston.info('error connecting to memcached', { error: e }); |
||||||
callback(value); |
}); |
||||||
|
}; |
||||||
// Update the key so that the expiration is pushed forward
|
|
||||||
if (value && !skipExpire) { |
// Save file in a key
|
||||||
this.set(key, data, (updateSucceeded) => { |
MemcachedDocumentStore.prototype.set = |
||||||
if (!updateSucceeded) { |
function(key, data, callback, skipExpire) { |
||||||
winston.error('failed to update expiration on GET', {key}); |
MemcachedDocumentStore.client.set(key, data, function(err, reply) { |
||||||
} |
err ? callback(false) : callback(true); |
||||||
}, skipExpire); |
}, skipExpire ? 0 : this.expire); |
||||||
} |
}; |
||||||
}); |
|
||||||
} |
// Get a file from a key
|
||||||
|
MemcachedDocumentStore.prototype.get = function(key, callback, skipExpire) { |
||||||
} |
var _this = this; |
||||||
|
MemcachedDocumentStore.client.get(key, function(err, reply) { |
||||||
|
callback(err ? false : reply); |
||||||
|
if (_this.expire && !skipExpire) { |
||||||
|
winston.warn('store does not currently push forward expirations on GET'); |
||||||
|
} |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
module.exports = MemcachedDocumentStore; |
module.exports = MemcachedDocumentStore; |
||||||
|
@ -1,88 +0,0 @@ |
|||||||
|
|
||||||
|
|
||||||
var MongoClient = require('mongodb').MongoClient, |
|
||||||
winston = require('winston'); |
|
||||||
|
|
||||||
var MongoDocumentStore = function (options) { |
|
||||||
this.expire = options.expire; |
|
||||||
this.connectionUrl = process.env.DATABASE_URl || options.connectionUrl; |
|
||||||
}; |
|
||||||
|
|
||||||
MongoDocumentStore.prototype.set = function (key, data, callback, skipExpire) { |
|
||||||
var now = Math.floor(new Date().getTime() / 1000), |
|
||||||
that = this; |
|
||||||
|
|
||||||
this.safeConnect(function (err, db) { |
|
||||||
if (err) |
|
||||||
return callback(false); |
|
||||||
|
|
||||||
db.collection('entries').update({ |
|
||||||
'entry_id': key, |
|
||||||
$or: [ |
|
||||||
{ expiration: -1 }, |
|
||||||
{ expiration: { $gt: now } } |
|
||||||
] |
|
||||||
}, { |
|
||||||
'entry_id': key, |
|
||||||
'value': data, |
|
||||||
'expiration': that.expire && !skipExpire ? that.expire + now : -1 |
|
||||||
}, { |
|
||||||
upsert: true |
|
||||||
}, function (err, existing) { |
|
||||||
if (err) { |
|
||||||
winston.error('error persisting value to mongodb', { error: err }); |
|
||||||
return callback(false); |
|
||||||
} |
|
||||||
|
|
||||||
callback(true); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
MongoDocumentStore.prototype.get = function (key, callback, skipExpire) { |
|
||||||
var now = Math.floor(new Date().getTime() / 1000), |
|
||||||
that = this; |
|
||||||
|
|
||||||
this.safeConnect(function (err, db) { |
|
||||||
if (err) |
|
||||||
return callback(false); |
|
||||||
|
|
||||||
db.collection('entries').findOne({ |
|
||||||
'entry_id': key, |
|
||||||
$or: [ |
|
||||||
{ expiration: -1 }, |
|
||||||
{ expiration: { $gt: now } } |
|
||||||
] |
|
||||||
}, function (err, entry) { |
|
||||||
if (err) { |
|
||||||
winston.error('error persisting value to mongodb', { error: err }); |
|
||||||
return callback(false); |
|
||||||
} |
|
||||||
|
|
||||||
callback(entry === null ? false : entry.value); |
|
||||||
|
|
||||||
if (entry !== null && entry.expiration !== -1 && that.expire && !skipExpire) { |
|
||||||
db.collection('entries').update({ |
|
||||||
'entry_id': key |
|
||||||
}, { |
|
||||||
$set: { |
|
||||||
'expiration': that.expire + now |
|
||||||
} |
|
||||||
}, function (err, result) { }); |
|
||||||
} |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
MongoDocumentStore.prototype.safeConnect = function (callback) { |
|
||||||
MongoClient.connect(this.connectionUrl, function (err, db) { |
|
||||||
if (err) { |
|
||||||
winston.error('error connecting to mongodb', { error: err }); |
|
||||||
callback(err); |
|
||||||
} else { |
|
||||||
callback(undefined, db); |
|
||||||
} |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
module.exports = MongoDocumentStore; |
|
@ -1,46 +0,0 @@ |
|||||||
const crypto = require('crypto'); |
|
||||||
const rethink = require('rethinkdbdash'); |
|
||||||
const winston = require('winston'); |
|
||||||
|
|
||||||
const md5 = (str) => { |
|
||||||
const md5sum = crypto.createHash('md5'); |
|
||||||
md5sum.update(str); |
|
||||||
return md5sum.digest('hex'); |
|
||||||
}; |
|
||||||
|
|
||||||
class RethinkDBStore { |
|
||||||
constructor(options) { |
|
||||||
this.client = rethink({ |
|
||||||
silent: true, |
|
||||||
host: options.host || '127.0.0.1', |
|
||||||
port: options.port || 28015, |
|
||||||
db: options.db || 'haste', |
|
||||||
user: options.user || 'admin', |
|
||||||
password: options.password || '' |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
set(key, data, callback) { |
|
||||||
this.client.table('uploads').insert({ id: md5(key), data: data }).run((error) => { |
|
||||||
if (error) { |
|
||||||
callback(false); |
|
||||||
winston.error('failed to insert to table', error); |
|
||||||
return; |
|
||||||
} |
|
||||||
callback(true); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
get(key, callback) { |
|
||||||
this.client.table('uploads').get(md5(key)).run((error, result) => { |
|
||||||
if (error || !result) { |
|
||||||
callback(false); |
|
||||||
if (error) winston.error('failed to insert to table', error); |
|
||||||
return; |
|
||||||
} |
|
||||||
callback(result.data); |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = RethinkDBStore; |
|
@ -1,32 +0,0 @@ |
|||||||
const fs = require('fs'); |
|
||||||
|
|
||||||
module.exports = class DictionaryGenerator { |
|
||||||
|
|
||||||
constructor(options, readyCallback) { |
|
||||||
// Check options format
|
|
||||||
if (!options) throw Error('No options passed to generator'); |
|
||||||
if (!options.path) throw Error('No dictionary path specified in options'); |
|
||||||
|
|
||||||
// Load dictionary
|
|
||||||
fs.readFile(options.path, 'utf8', (err, data) => { |
|
||||||
if (err) throw err; |
|
||||||
|
|
||||||
this.dictionary = data.split(/[\n\r]+/); |
|
||||||
|
|
||||||
if (readyCallback) readyCallback(); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
// Generates a dictionary-based key, of keyLength words
|
|
||||||
createKey(keyLength) { |
|
||||||
let text = ''; |
|
||||||
|
|
||||||
for (let i = 0; i < keyLength; i++) { |
|
||||||
const index = Math.floor(Math.random() * this.dictionary.length); |
|
||||||
text += this.dictionary[index]; |
|
||||||
} |
|
||||||
|
|
||||||
return text; |
|
||||||
} |
|
||||||
|
|
||||||
}; |
|
@ -1,27 +1,33 @@ |
|||||||
// Draws inspiration from pwgen and http://tools.arantius.com/password
|
// Draws inspiration from pwgen and http://tools.arantius.com/password
|
||||||
|
var PhoneticKeyGenerator = function(options) { |
||||||
const randOf = (collection) => { |
// No options
|
||||||
return () => { |
|
||||||
return collection[Math.floor(Math.random() * collection.length)]; |
|
||||||
}; |
|
||||||
}; |
}; |
||||||
|
|
||||||
// Helper methods to get an random vowel or consonant
|
// Generate a phonetic key
|
||||||
const randVowel = randOf('aeiou'); |
PhoneticKeyGenerator.prototype.createKey = function(keyLength) { |
||||||
const randConsonant = randOf('bcdfghjklmnpqrstvwxyz'); |
var text = ''; |
||||||
|
var start = Math.round(Math.random()); |
||||||
module.exports = class PhoneticKeyGenerator { |
for (var i = 0; i < keyLength; i++) { |
||||||
|
text += (i % 2 == start) ? this.randConsonant() : this.randVowel(); |
||||||
// Generate a phonetic key of alternating consonant & vowel
|
} |
||||||
createKey(keyLength) { |
return text; |
||||||
let text = ''; |
}; |
||||||
const start = Math.round(Math.random()); |
|
||||||
|
|
||||||
for (let i = 0; i < keyLength; i++) { |
PhoneticKeyGenerator.consonants = 'bcdfghjklmnpqrstvwxyz'; |
||||||
text += (i % 2 == start) ? randConsonant() : randVowel(); |
PhoneticKeyGenerator.vowels = 'aeiou'; |
||||||
} |
|
||||||
|
|
||||||
return text; |
// Get an random vowel
|
||||||
} |
PhoneticKeyGenerator.prototype.randVowel = function() { |
||||||
|
return PhoneticKeyGenerator.vowels[ |
||||||
|
Math.floor(Math.random() * PhoneticKeyGenerator.vowels.length) |
||||||
|
]; |
||||||
|
}; |
||||||
|
|
||||||
|
// Get an random consonant
|
||||||
|
PhoneticKeyGenerator.prototype.randConsonant = function() { |
||||||
|
return PhoneticKeyGenerator.consonants[ |
||||||
|
Math.floor(Math.random() * PhoneticKeyGenerator.consonants.length) |
||||||
|
]; |
||||||
}; |
}; |
||||||
|
|
||||||
|
module.exports = PhoneticKeyGenerator; |
||||||
|
@ -1,20 +1,19 @@ |
|||||||
module.exports = class RandomKeyGenerator { |
var RandomKeyGenerator = function(options) { |
||||||
|
if (!options) { |
||||||
// Initialize a new generator with the given keySpace
|
options = {}; |
||||||
constructor(options = {}) { |
|
||||||
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
|
||||||
} |
} |
||||||
|
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
||||||
|
}; |
||||||
|
|
||||||
// Generate a key of the given length
|
// Generate a random key
|
||||||
createKey(keyLength) { |
RandomKeyGenerator.prototype.createKey = function(keyLength) { |
||||||
var text = ''; |
var text = ''; |
||||||
|
var index; |
||||||
for (var i = 0; i < keyLength; i++) { |
for (var i = 0; i < keyLength; i++) { |
||||||
const index = Math.floor(Math.random() * this.keyspace.length); |
index = Math.floor(Math.random() * this.keyspace.length); |
||||||
text += this.keyspace.charAt(index); |
text += this.keyspace.charAt(index); |
||||||
} |
|
||||||
|
|
||||||
return text; |
|
||||||
} |
} |
||||||
|
return text; |
||||||
}; |
}; |
||||||
|
|
||||||
|
module.exports = RandomKeyGenerator; |
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,34 +0,0 @@ |
|||||||
/* global describe, it */ |
|
||||||
|
|
||||||
const assert = require('assert'); |
|
||||||
|
|
||||||
const fs = require('fs'); |
|
||||||
|
|
||||||
const Generator = require('../../lib/key_generators/dictionary'); |
|
||||||
|
|
||||||
describe('DictionaryGenerator', function() { |
|
||||||
describe('options', function() { |
|
||||||
it('should throw an error if given no options', () => { |
|
||||||
assert.throws(() => { |
|
||||||
new Generator(); |
|
||||||
}, Error); |
|
||||||
}); |
|
||||||
|
|
||||||
it('should throw an error if given no path', () => { |
|
||||||
assert.throws(() => { |
|
||||||
new Generator({}); |
|
||||||
}, Error); |
|
||||||
}); |
|
||||||
}); |
|
||||||
describe('generation', function() { |
|
||||||
it('should return a key of the proper number of words from the given dictionary', () => { |
|
||||||
const path = '/tmp/haste-server-test-dictionary'; |
|
||||||
const words = ['cat']; |
|
||||||
fs.writeFileSync(path, words.join('\n')); |
|
||||||
|
|
||||||
const gen = new Generator({path}, () => { |
|
||||||
assert.equal('catcatcat', gen.createKey(3)); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}); |
|
@ -1,35 +0,0 @@ |
|||||||
/* global describe, it */ |
|
||||||
|
|
||||||
const assert = require('assert'); |
|
||||||
|
|
||||||
const Generator = require('../../lib/key_generators/phonetic'); |
|
||||||
|
|
||||||
const vowels = 'aeiou'; |
|
||||||
const consonants = 'bcdfghjklmnpqrstvwxyz'; |
|
||||||
|
|
||||||
describe('PhoneticKeyGenerator', () => { |
|
||||||
describe('generation', () => { |
|
||||||
it('should return a key of the proper length', () => { |
|
||||||
const gen = new Generator(); |
|
||||||
assert.equal(6, gen.createKey(6).length); |
|
||||||
}); |
|
||||||
|
|
||||||
it('should alternate consonants and vowels', () => { |
|
||||||
const gen = new Generator(); |
|
||||||
|
|
||||||
const key = gen.createKey(3); |
|
||||||
|
|
||||||
// if it starts with a consonant, we expect cvc
|
|
||||||
// if it starts with a vowel, we expect vcv
|
|
||||||
if(consonants.includes(key[0])) { |
|
||||||
assert.ok(consonants.includes(key[0])); |
|
||||||
assert.ok(consonants.includes(key[2])); |
|
||||||
assert.ok(vowels.includes(key[1])); |
|
||||||
} else { |
|
||||||
assert.ok(vowels.includes(key[0])); |
|
||||||
assert.ok(vowels.includes(key[2])); |
|
||||||
assert.ok(consonants.includes(key[1])); |
|
||||||
} |
|
||||||
}); |
|
||||||
}); |
|
||||||
}); |
|
@ -1,24 +0,0 @@ |
|||||||
/* global describe, it */ |
|
||||||
|
|
||||||
const assert = require('assert'); |
|
||||||
|
|
||||||
const Generator = require('../../lib/key_generators/random'); |
|
||||||
|
|
||||||
describe('RandomKeyGenerator', () => { |
|
||||||
describe('generation', () => { |
|
||||||
it('should return a key of the proper length', () => { |
|
||||||
const gen = new Generator(); |
|
||||||
assert.equal(gen.createKey(6).length, 6); |
|
||||||
}); |
|
||||||
|
|
||||||
it('should use a key from the given keyset if given', () => { |
|
||||||
const gen = new Generator({keyspace: 'A'}); |
|
||||||
assert.equal(gen.createKey(6), 'AAAAAA'); |
|
||||||
}); |
|
||||||
|
|
||||||
it('should not use a key from the given keyset if not given', () => { |
|
||||||
const gen = new Generator({keyspace: 'A'}); |
|
||||||
assert.ok(!gen.createKey(6).includes('B')); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}); |
|
Loading…
Reference in new issue