mirror of
https://github.com/seejohnrun/haste-server.git
synced 2024-12-25 03:26:59 +00:00
Merge branch 'master' into production
This commit is contained in:
commit
4e3620139d
30 changed files with 2388 additions and 144 deletions
6
.dockerignore
Normal file
6
.dockerignore
Normal file
|
@ -0,0 +1,6 @@
|
|||
npm-debug.log
|
||||
node_modules
|
||||
*.swp
|
||||
*.swo
|
||||
data
|
||||
*.DS_Store
|
2
.eslintignore
Normal file
2
.eslintignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
**/*.min.js
|
||||
config.js
|
25
.eslintrc.json
Normal file
25
.eslintrc.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
}
|
63
Dockerfile
Normal file
63
Dockerfile
Normal file
|
@ -0,0 +1,63 @@
|
|||
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_USENAMER= \
|
||||
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 [ "curl" , "-f" "localhost:${PORT}", "||", "exit", "1"]
|
||||
CMD ["npm", "start"]
|
154
README.md
154
README.md
|
@ -38,9 +38,9 @@ STDOUT. Check the README there for more details and usages.
|
|||
* `host` - the host the server runs on (default localhost)
|
||||
* `port` - the port the server runs on (default 7777)
|
||||
* `keyLength` - the length of the keys to user (default 10)
|
||||
* `maxLength` - maximum length of a paste (default none)
|
||||
* `maxLength` - maximum length of a paste (default 400000)
|
||||
* `staticMaxAge` - max age for static assets (86400)
|
||||
* `recompressStatisAssets` - whether or not to compile static js assets (true)
|
||||
* `recompressStaticAssets` - whether or not to compile static js assets (true)
|
||||
* `documents` - static documents to serve (ex: http://hastebin.com/about.com)
|
||||
in addition to static assets. These will never expire.
|
||||
* `storage` - storage options (see below)
|
||||
|
@ -97,7 +97,9 @@ something like:
|
|||
}
|
||||
```
|
||||
|
||||
Where `path` represents where you want the files stored
|
||||
where `path` represents where you want the files stored.
|
||||
|
||||
File storage currently does not support paste expiration, you can follow [#191](https://github.com/seejohnrun/haste-server/issues/191) for status updates.
|
||||
|
||||
### Redis
|
||||
|
||||
|
@ -154,9 +156,9 @@ All of which are optional except `type` with very logical default values.
|
|||
|
||||
### Memcached
|
||||
|
||||
To use memcached storage you must install the `memcache` package via npm
|
||||
To use memcache storage you must install the `memcached` package via npm
|
||||
|
||||
`npm install memcache`
|
||||
`npm install memcached`
|
||||
|
||||
Once you've done that, your config section should look like:
|
||||
|
||||
|
@ -174,6 +176,148 @@ forward on GETs.
|
|||
|
||||
All of which are optional except `type` with very logical default values.
|
||||
|
||||
### RethinkDB
|
||||
|
||||
To use the RethinkDB storage system, you must install the `rethinkdbdash` package via npm
|
||||
|
||||
`npm install rethinkdbdash`
|
||||
|
||||
Once you've done that, your config section should look like this:
|
||||
|
||||
``` json
|
||||
{
|
||||
"type": "rethinkdb",
|
||||
"host": "127.0.0.1",
|
||||
"port": 28015,
|
||||
"db": "haste"
|
||||
}
|
||||
```
|
||||
|
||||
In order for this to work, the database must be pre-created before the script is ran.
|
||||
Also, you must create an `uploads` table, which will store all the data for uploads.
|
||||
|
||||
You can optionally add the `user` and `password` properties to use a user system.
|
||||
|
||||
### Amazon S3
|
||||
|
||||
To use [Amazon S3](https://aws.amazon.com/s3/) as a storage system, you must
|
||||
install the `aws-sdk` package via npm:
|
||||
|
||||
`npm install aws-sdk`
|
||||
|
||||
Once you've done that, your config section should look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "amazon-s3",
|
||||
"bucket": "your-bucket-name",
|
||||
"region": "us-east-1"
|
||||
}
|
||||
```
|
||||
|
||||
Authentication is handled automatically by the client. Check
|
||||
[Amazon's documentation](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html)
|
||||
for more information. You will need to grant your role these permissions to
|
||||
your bucket:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"s3:GetObject",
|
||||
"s3:PutObject"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": "arn:aws:s3:::your-bucket-name-goes-here/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
### Build image
|
||||
|
||||
```bash
|
||||
docker build --tag haste-server .
|
||||
```
|
||||
|
||||
### Run container
|
||||
|
||||
For this example we will run haste-server, and connect it to a redis server
|
||||
|
||||
```bash
|
||||
docker run --name haste-server-container --env STORAGE_TYPE=redis --env STORAGE_HOST=redis-server --env STORAGE_PORT=6379 haste-server
|
||||
```
|
||||
|
||||
### Use docker-compose example
|
||||
|
||||
There is an example `docker-compose.yml` which runs haste-server together with memcached
|
||||
|
||||
```bash
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
The docker image is configured using environmental variables as you can see in the example above.
|
||||
|
||||
Here is a list of all the environment variables
|
||||
|
||||
### Storage
|
||||
|
||||
| Name | Default value | Description |
|
||||
| :--------------------: | :-----------: | :-----------------------------------------------------------------------------------------------------------: |
|
||||
| STORAGE_TYPE | memcached | Type of storage . Accepted values: "memcached","redis","postgres","rethinkdb", "amazon-s3", and "file" |
|
||||
| STORAGE_HOST | 127.0.0.1 | Storage host. Applicable for types: memcached, redis, postgres, and rethinkdb |
|
||||
| STORAGE_PORT | 11211 | Port on the storage host. Applicable for types: memcached, redis, postgres, and rethinkdb |
|
||||
| STORAGE_EXPIRE_SECONDS | 2592000 | Number of seconds to expire keys in. Applicable for types. Redis, postgres, memcached. `expire` option to the |
|
||||
| STORAGE_DB | 2 | The name of the database. Applicable for redis, postgres, and rethinkdb |
|
||||
| STORAGE_PASSWORD | | Password for database. Applicable for redis, postges, rethinkdb . |
|
||||
| STORAGE_USERNAME | | Database username. Applicable for postgres, and rethinkdb |
|
||||
| STORAGE_AWS_BUCKET | | Applicable for amazon-s3. This is the name of the S3 bucket |
|
||||
| STORAGE_AWS_REGION | | Applicable for amazon-s3. The region in which the bucket is located |
|
||||
| STORAGE_FILEPATH | | Path to file to save data to. Applicable for type file |
|
||||
|
||||
### Logging
|
||||
|
||||
| Name | Default value | Description |
|
||||
| :---------------: | :-----------: | :---------: |
|
||||
| LOGGING_LEVEL | verbose | |
|
||||
| LOGGING_TYPE= | Console |
|
||||
| LOGGING_COLORIZE= | true |
|
||||
|
||||
### Basics
|
||||
|
||||
| Name | Default value | Description |
|
||||
| :----------------------: | :--------------: | :---------------------------------------------------------------------------------------: |
|
||||
| HOST | 0.0.0.0 | The hostname which the server answers on |
|
||||
| PORT | 7777 | The port on which the server is running |
|
||||
| KEY_LENGTH | 10 | the length of the keys to user |
|
||||
| MAX_LENGTH | 400000 | maximum length of a paste |
|
||||
| STATIC_MAX_AGE | 86400 | max age for static assets |
|
||||
| RECOMPRESS_STATIC_ASSETS | true | whether or not to compile static js assets |
|
||||
| KEYGENERATOR_TYPE | phonetic | Type of key generator. Acceptable values: "phonetic", or "random" |
|
||||
| KEYGENERATOR_KEYSPACE | | keySpace argument is a string of acceptable characters |
|
||||
| DOCUMENTS | about=./about.md | Comma separated list of static documents to serve. ex: \n about=./about.md,home=./home.md |
|
||||
|
||||
### Rate limits
|
||||
|
||||
| Name | Default value | Description |
|
||||
| :----------------------------------: | :-----------------------------------: | :--------------------------------------------------------------------------------------: |
|
||||
| RATELIMITS_NORMAL_TOTAL_REQUESTS | 500 | By default anyone uncategorized will be subject to 500 requests in the defined timespan. |
|
||||
| RATELIMITS_NORMAL_EVERY_MILLISECONDS | 60000 | The timespan to allow the total requests for uncategorized users |
|
||||
| RATELIMITS_WHITELIST_TOTAL_REQUESTS | | By default client names in the whitelist will not have their requests limited. |
|
||||
| RATELIMITS_WHITELIST_EVERY_SECONDS | | By default client names in the whitelist will not have their requests limited. |
|
||||
| RATELIMITS_WHITELIST | example1.whitelist,example2.whitelist | Comma separated list of the clients which are in the whitelist pool |
|
||||
| RATELIMITS_BLACKLIST_TOTAL_REQUESTS | | By default client names in the blacklist will be subject to 0 requests per hours. |
|
||||
| RATELIMITS_BLACKLIST_EVERY_SECONDS | | By default client names in the blacklist will be subject to 0 requests per hours |
|
||||
| RATELIMITS_BLACKLIST | example1.blacklist,example2.blacklist | Comma separated list of the clients which are in the blacklistpool. |
|
||||
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
John Crepezzi <john.crepezzi@gmail.com>
|
||||
|
|
2
about.md
2
about.md
|
@ -19,7 +19,7 @@ Most of the time I want to show you some text, it's coming from my current
|
|||
console session. We should make it really easy to take code from the console
|
||||
and send it to people.
|
||||
|
||||
`cat something | haste` # http://hastebin.com/1238193
|
||||
`cat something | haste` # https://hastebin.com/1238193
|
||||
|
||||
You can even take this a step further, and cut out the last step of copying the
|
||||
URL with:
|
||||
|
|
19
docker-compose.yaml
Normal file
19
docker-compose.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
version: '3.0'
|
||||
services:
|
||||
haste-server:
|
||||
build: .
|
||||
networks:
|
||||
- db-network
|
||||
environment:
|
||||
- STORAGE_TYPE=memcached
|
||||
- STORAGE_HOST=memcached
|
||||
- STORAGE_PORT=11211
|
||||
ports:
|
||||
- 7777:7777
|
||||
memcached:
|
||||
image: memcached:latest
|
||||
networks:
|
||||
- db-network
|
||||
|
||||
networks:
|
||||
db-network:
|
108
docker-entrypoint.js
Normal file
108
docker-entrypoint.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
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: PORT,
|
||||
|
||||
keyLength: KEY_LENGTH,
|
||||
|
||||
maxLength: MAX_LENGTH,
|
||||
|
||||
staticMaxAge: 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: STORAGE_PORT,
|
||||
expire: 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));
|
9
docker-entrypoint.sh
Normal file
9
docker-entrypoint.sh
Normal file
|
@ -0,0 +1,9 @@
|
|||
#!/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 "$@"
|
|
@ -36,7 +36,7 @@ DocumentHandler.prototype.handleRawGet = function(key, response, skipExpire) {
|
|||
this.store.get(key, function(ret) {
|
||||
if (ret) {
|
||||
winston.verbose('retrieved raw document', { key: key });
|
||||
response.writeHead(200, { 'content-type': 'text/plain' });
|
||||
response.writeHead(200, { 'content-type': 'text/plain; charset=UTF-8' });
|
||||
response.end(ret);
|
||||
}
|
||||
else {
|
||||
|
@ -124,7 +124,7 @@ DocumentHandler.prototype.chooseKey = function(callback) {
|
|||
} else {
|
||||
callback(key);
|
||||
}
|
||||
});
|
||||
}, true); // Don't bump expirations when key searching
|
||||
};
|
||||
|
||||
DocumentHandler.prototype.acceptableKey = function() {
|
||||
|
|
56
lib/document_stores/amazon-s3.js
Normal file
56
lib/document_stores/amazon-s3.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*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,45 +1,52 @@
|
|||
var memcached = require('memcache');
|
||||
var winston = require('winston');
|
||||
const memcached = require('memcached');
|
||||
const winston = require('winston');
|
||||
|
||||
// Create a new store with options
|
||||
var MemcachedDocumentStore = function(options) {
|
||||
this.expire = options.expire;
|
||||
if (!MemcachedDocumentStore.client) {
|
||||
MemcachedDocumentStore.connect(options);
|
||||
class MemcachedDocumentStore {
|
||||
|
||||
// Create a new store with options
|
||||
constructor(options) {
|
||||
this.expire = options.expire;
|
||||
|
||||
const host = options.host || '127.0.0.1';
|
||||
const port = options.port || 11211;
|
||||
const url = `${host}:${port}`;
|
||||
this.connect(url);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a connection
|
||||
MemcachedDocumentStore.connect = function(options) {
|
||||
var host = options.host || '127.0.0.1';
|
||||
var port = options.port || 11211;
|
||||
this.client = new memcached.Client(port, host);
|
||||
this.client.connect();
|
||||
this.client.on('connect', function() {
|
||||
winston.info('connected to memcached on ' + host + ':' + port);
|
||||
});
|
||||
this.client.on('error', function(e) {
|
||||
winston.info('error connecting to memcached', { error: e });
|
||||
});
|
||||
};
|
||||
// Create a connection
|
||||
connect(url) {
|
||||
this.client = new memcached(url);
|
||||
|
||||
// Save file in a key
|
||||
MemcachedDocumentStore.prototype.set =
|
||||
function(key, data, callback, skipExpire) {
|
||||
MemcachedDocumentStore.client.set(key, data, function(err, reply) {
|
||||
err ? callback(false) : callback(true);
|
||||
}, skipExpire ? 0 : this.expire);
|
||||
};
|
||||
winston.info(`connecting to memcached on ${url}`);
|
||||
|
||||
// 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');
|
||||
}
|
||||
});
|
||||
};
|
||||
this.client.on('failure', function(error) {
|
||||
winston.info('error connecting to memcached', {error});
|
||||
});
|
||||
}
|
||||
|
||||
// Save file in a key
|
||||
set(key, data, callback, skipExpire) {
|
||||
this.client.set(key, data, skipExpire ? 0 : this.expire, (error) => {
|
||||
callback(!error);
|
||||
});
|
||||
}
|
||||
|
||||
// Get a file from a key
|
||||
get(key, callback, skipExpire) {
|
||||
this.client.get(key, (error, data) => {
|
||||
callback(error ? false : data);
|
||||
|
||||
// Update the key so that the expiration is pushed forward
|
||||
if (!skipExpire) {
|
||||
this.set(key, data, (updateSucceeded) => {
|
||||
if (!updateSucceeded) {
|
||||
winston.error('failed to update expiration on GET', {key});
|
||||
}
|
||||
}, skipExpire);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = MemcachedDocumentStore;
|
||||
|
|
|
@ -23,7 +23,7 @@ PostgresDocumentStore.prototype = {
|
|||
key,
|
||||
data,
|
||||
that.expireJS && !skipExpire ? that.expireJS + now : null
|
||||
], function (err, result) {
|
||||
], function (err) {
|
||||
if (err) {
|
||||
winston.error('error persisting value to postgres', { error: err });
|
||||
return callback(false);
|
||||
|
@ -50,7 +50,7 @@ PostgresDocumentStore.prototype = {
|
|||
client.query('UPDATE entries SET expiration = $1 WHERE ID = $2', [
|
||||
that.expireJS + now,
|
||||
result.rows[0].id
|
||||
], function (err, result) {
|
||||
], function (err) {
|
||||
if (!err) {
|
||||
done();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,12 @@ RedisDocumentStore.connect = function(options) {
|
|||
if (options.password) {
|
||||
RedisDocumentStore.client.auth(options.password);
|
||||
}
|
||||
RedisDocumentStore.client.select(index, function(err, reply) {
|
||||
|
||||
RedisDocumentStore.client.on('error', function(err) {
|
||||
winston.error('redis disconnected', err);
|
||||
});
|
||||
|
||||
RedisDocumentStore.client.select(index, function(err) {
|
||||
if (err) {
|
||||
winston.error(
|
||||
'error connecting to redis index ' + index,
|
||||
|
@ -46,7 +51,7 @@ RedisDocumentStore.connect = function(options) {
|
|||
// Save file in a key
|
||||
RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||
var _this = this;
|
||||
RedisDocumentStore.client.set(key, data, function(err, reply) {
|
||||
RedisDocumentStore.client.set(key, data, function(err) {
|
||||
if (err) {
|
||||
callback(false);
|
||||
}
|
||||
|
@ -62,7 +67,7 @@ RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
|||
// Expire a key in expire time if set
|
||||
RedisDocumentStore.prototype.setExpiration = function(key) {
|
||||
if (this.expire) {
|
||||
RedisDocumentStore.client.expire(key, this.expire, function(err, reply) {
|
||||
RedisDocumentStore.client.expire(key, this.expire, function(err) {
|
||||
if (err) {
|
||||
winston.error('failed to set expiry on key: ' + key);
|
||||
}
|
||||
|
|
46
lib/document_stores/rethinkdb.js
Normal file
46
lib/document_stores/rethinkdb.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
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;
|
32
lib/key_generators/dictionary.js
Normal file
32
lib/key_generators/dictionary.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
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,33 +1,27 @@
|
|||
// Draws inspiration from pwgen and http://tools.arantius.com/password
|
||||
var PhoneticKeyGenerator = function(options) {
|
||||
// No options
|
||||
|
||||
const randOf = (collection) => {
|
||||
return () => {
|
||||
return collection[Math.floor(Math.random() * collection.length)];
|
||||
};
|
||||
};
|
||||
|
||||
// Generate a phonetic key
|
||||
PhoneticKeyGenerator.prototype.createKey = function(keyLength) {
|
||||
var text = '';
|
||||
var start = Math.round(Math.random());
|
||||
for (var i = 0; i < keyLength; i++) {
|
||||
text += (i % 2 == start) ? this.randConsonant() : this.randVowel();
|
||||
// Helper methods to get an random vowel or consonant
|
||||
const randVowel = randOf('aeiou');
|
||||
const randConsonant = randOf('bcdfghjklmnpqrstvwxyz');
|
||||
|
||||
module.exports = class PhoneticKeyGenerator {
|
||||
|
||||
// Generate a phonetic key of alternating consonant & vowel
|
||||
createKey(keyLength) {
|
||||
let text = '';
|
||||
const start = Math.round(Math.random());
|
||||
|
||||
for (let i = 0; i < keyLength; i++) {
|
||||
text += (i % 2 == start) ? randConsonant() : randVowel();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
return text;
|
||||
|
||||
};
|
||||
|
||||
PhoneticKeyGenerator.consonants = 'bcdfghjklmnpqrstvwxyz';
|
||||
PhoneticKeyGenerator.vowels = 'aeiou';
|
||||
|
||||
// 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,19 +1,20 @@
|
|||
var RandomKeyGenerator = function(options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
};
|
||||
module.exports = class RandomKeyGenerator {
|
||||
|
||||
// Generate a random key
|
||||
RandomKeyGenerator.prototype.createKey = function(keyLength) {
|
||||
var text = '';
|
||||
var index;
|
||||
for (var i = 0; i < keyLength; i++) {
|
||||
index = Math.floor(Math.random() * this.keyspace.length);
|
||||
text += this.keyspace.charAt(index);
|
||||
// Initialize a new generator with the given keySpace
|
||||
constructor(options = {}) {
|
||||
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
module.exports = RandomKeyGenerator;
|
||||
// Generate a key of the given length
|
||||
createKey(keyLength) {
|
||||
var text = '';
|
||||
|
||||
for (var i = 0; i < keyLength; i++) {
|
||||
const index = Math.floor(Math.random() * this.keyspace.length);
|
||||
text += this.keyspace.charAt(index);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
1639
package-lock.json
generated
Normal file
1639
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
17
package.json
17
package.json
|
@ -14,18 +14,17 @@
|
|||
},
|
||||
"main": "haste",
|
||||
"dependencies": {
|
||||
"busboy": "0.2.4",
|
||||
"connect": "^3.7.0",
|
||||
"connect-ratelimit": "0.0.7",
|
||||
"connect-route": "0.1.5",
|
||||
"connect": "3.4.1",
|
||||
"st": "1.1.0",
|
||||
"winston": "0.6.2",
|
||||
"uglify-js": "1.3.3",
|
||||
"busboy": "0.2.4",
|
||||
"pg": "4.1.1"
|
||||
"st": "^2.0.0",
|
||||
"winston": "^2.0.0",
|
||||
"uglify-js": "3.1.6",
|
||||
"pg": "^8.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "*",
|
||||
"should": "*"
|
||||
"mocha": "^8.1.3"
|
||||
},
|
||||
"bundledDependencies": [],
|
||||
"engines": {
|
||||
|
@ -44,6 +43,6 @@
|
|||
},
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"test": "mocha -r should spec/*"
|
||||
"test": "mocha --recursive"
|
||||
}
|
||||
}
|
||||
|
|
34
server.js
34
server.js
|
@ -1,7 +1,7 @@
|
|||
var http = require('http');
|
||||
var url = require('url');
|
||||
var fs = require('fs');
|
||||
|
||||
var uglify = require('uglify-js');
|
||||
var winston = require('winston');
|
||||
var connect = require('connect');
|
||||
var route = require('connect-route');
|
||||
|
@ -19,7 +19,10 @@ config.host = process.env.HOST || config.host || 'localhost';
|
|||
if (config.logging) {
|
||||
try {
|
||||
winston.remove(winston.transports.Console);
|
||||
} catch(er) { }
|
||||
} catch(e) {
|
||||
/* was not present */
|
||||
}
|
||||
|
||||
var detail, type;
|
||||
for (var i = 0; i < config.logging.length; i++) {
|
||||
detail = config.logging[i];
|
||||
|
@ -52,21 +55,14 @@ else {
|
|||
|
||||
// Compress the static javascript assets
|
||||
if (config.recompressStaticAssets) {
|
||||
var jsp = require("uglify-js").parser;
|
||||
var pro = require("uglify-js").uglify;
|
||||
var list = fs.readdirSync('./static');
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var item = list[i];
|
||||
var orig_code, ast;
|
||||
if ((item.indexOf('.js') === item.length - 3) &&
|
||||
(item.indexOf('.min.js') === -1)) {
|
||||
dest = item.substring(0, item.length - 3) + '.min' +
|
||||
item.substring(item.length - 3);
|
||||
orig_code = fs.readFileSync('./static/' + item, 'utf8');
|
||||
ast = jsp.parse(orig_code);
|
||||
ast = pro.ast_mangle(ast);
|
||||
ast = pro.ast_squeeze(ast);
|
||||
fs.writeFileSync('./static/' + dest, pro.gen_code(ast), 'utf8');
|
||||
for (var j = 0; j < list.length; j++) {
|
||||
var item = list[j];
|
||||
if ((item.indexOf('.js') === item.length - 3) && (item.indexOf('.min.js') === -1)) {
|
||||
var dest = item.substring(0, item.length - 3) + '.min' + item.substring(item.length - 3);
|
||||
var orig_code = fs.readFileSync('./static/' + item, 'utf8');
|
||||
|
||||
fs.writeFileSync('./static/' + dest, uglify.minify(orig_code).code, 'utf8');
|
||||
winston.info('compressed ' + item + ' into ' + dest);
|
||||
}
|
||||
}
|
||||
|
@ -113,17 +109,17 @@ if (config.rateLimits) {
|
|||
// first look at API calls
|
||||
app.use(route(function(router) {
|
||||
// get raw documents - support getting with extension
|
||||
router.get('/raw/:id', function(request, response, next) {
|
||||
router.get('/raw/:id', function(request, response) {
|
||||
var key = request.params.id.split('.')[0];
|
||||
var skipExpire = !!config.documents[key];
|
||||
return documentHandler.handleRawGet(key, response, skipExpire);
|
||||
});
|
||||
// add documents
|
||||
router.post('/documents', function(request, response, next) {
|
||||
router.post('/documents', function(request, response) {
|
||||
return documentHandler.handlePost(request, response);
|
||||
});
|
||||
// get documents
|
||||
router.get('/documents/:id', function(request, response, next) {
|
||||
router.get('/documents/:id', function(request, response) {
|
||||
var key = request.params.id.split('.')[0];
|
||||
var skipExpire = !!config.documents[key];
|
||||
return documentHandler.handleGet(key, response, skipExpire);
|
||||
|
|
|
@ -17,6 +17,8 @@ textarea {
|
|||
outline: none;
|
||||
resize: none;
|
||||
font-size: 13px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* the line numbers */
|
||||
|
@ -42,7 +44,6 @@ textarea {
|
|||
border: 0px;
|
||||
outline: none;
|
||||
font-size: 13px;
|
||||
padding-right: 360px;
|
||||
overflow: inherit;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* global $, hljs, window, document */
|
||||
|
||||
///// represents a single document
|
||||
|
||||
var haste_document = function() {
|
||||
|
@ -42,10 +44,10 @@ haste_document.prototype.load = function(key, callback, lang) {
|
|||
value: high.value,
|
||||
key: key,
|
||||
language: high.language || lang,
|
||||
lineCount: res.data.split("\n").length
|
||||
lineCount: res.data.split('\n').length
|
||||
});
|
||||
},
|
||||
error: function(err) {
|
||||
error: function() {
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
|
@ -71,7 +73,7 @@ haste_document.prototype.save = function(data, callback) {
|
|||
value: high.value,
|
||||
key: res.key,
|
||||
language: high.language,
|
||||
lineCount: data.split("\n").length
|
||||
lineCount: data.split('\n').length
|
||||
});
|
||||
},
|
||||
error: function(res) {
|
||||
|
@ -168,8 +170,7 @@ haste.extensionMap = {
|
|||
lua: 'lua', pas: 'delphi', java: 'java', cpp: 'cpp', cc: 'cpp', m: 'objectivec',
|
||||
vala: 'vala', sql: 'sql', sm: 'smalltalk', lisp: 'lisp', ini: 'ini',
|
||||
diff: 'diff', bash: 'bash', sh: 'bash', tex: 'tex', erl: 'erlang', hs: 'haskell',
|
||||
md: 'markdown', txt: '', coffee: 'coffee', json: 'javascript',
|
||||
swift: 'swift'
|
||||
md: 'markdown', txt: '', coffee: 'coffee', swift: 'swift'
|
||||
};
|
||||
|
||||
// Look up the extension preferred for a type
|
||||
|
@ -276,7 +277,7 @@ haste.prototype.configureButtons = function() {
|
|||
$where: $('#box2 .new'),
|
||||
label: 'New',
|
||||
shortcut: function(evt) {
|
||||
return evt.ctrlKey && evt.keyCode === 78
|
||||
return evt.ctrlKey && evt.keyCode === 78;
|
||||
},
|
||||
shortcutDescription: 'control + n',
|
||||
action: function() {
|
||||
|
@ -331,14 +332,14 @@ haste.prototype.configureButton = function(options) {
|
|||
}
|
||||
});
|
||||
// Show the label
|
||||
options.$where.mouseenter(function(evt) {
|
||||
options.$where.mouseenter(function() {
|
||||
$('#box3 .label').text(options.label);
|
||||
$('#box3 .shortcut').text(options.shortcutDescription || '');
|
||||
$('#box3').show();
|
||||
$(this).append($('#pointer').remove().show());
|
||||
});
|
||||
// Hide the label
|
||||
options.$where.mouseleave(function(evt) {
|
||||
options.$where.mouseleave(function() {
|
||||
$('#box3').hide();
|
||||
$('#pointer').hide();
|
||||
});
|
||||
|
@ -371,7 +372,7 @@ $(function() {
|
|||
// For browsers like Internet Explorer
|
||||
if (document.selection) {
|
||||
this.focus();
|
||||
sel = document.selection.createRange();
|
||||
var sel = document.selection.createRange();
|
||||
sel.text = myValue;
|
||||
this.focus();
|
||||
}
|
||||
|
|
2
static/application.min.js
vendored
2
static/application.min.js
vendored
File diff suppressed because one or more lines are too long
8
static/highlight.min.js
vendored
8
static/highlight.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,7 @@
|
|||
/* global describe, it */
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var DocumentHandler = require('../lib/document_handler');
|
||||
var Generator = require('../lib/key_generators/random');
|
||||
|
||||
|
@ -8,13 +12,13 @@ describe('document_handler', function() {
|
|||
it('should choose a key of the proper length', function() {
|
||||
var gen = new Generator();
|
||||
var dh = new DocumentHandler({ keyLength: 6, keyGenerator: gen });
|
||||
dh.acceptableKey().length.should.equal(6);
|
||||
assert.equal(6, dh.acceptableKey().length);
|
||||
});
|
||||
|
||||
it('should choose a default key length', function() {
|
||||
var gen = new Generator();
|
||||
var dh = new DocumentHandler({ keyGenerator: gen });
|
||||
dh.keyLength.should.equal(DocumentHandler.defaultKeyLength);
|
||||
assert.equal(dh.keyLength, DocumentHandler.defaultKeyLength);
|
||||
});
|
||||
|
||||
});
|
33
test/key_generators/dictionary_spec.js
Normal file
33
test/key_generators/dictionary_spec.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* global describe, it */
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const Generator = require('../../lib/key_generators/dictionary');
|
||||
|
||||
describe('RandomKeyGenerator', function() {
|
||||
describe('randomKey', 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);
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
27
test/key_generators/phonetic_spec.js
Normal file
27
test/key_generators/phonetic_spec.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* global describe, it */
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const Generator = require('../../lib/key_generators/phonetic');
|
||||
|
||||
const vowels = 'aeiou';
|
||||
const consonants = 'bcdfghjklmnpqrstvwxyz';
|
||||
|
||||
describe('RandomKeyGenerator', () => {
|
||||
describe('randomKey', () => {
|
||||
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);
|
||||
|
||||
assert.ok(consonants.includes(key[0]));
|
||||
assert.ok(consonants.includes(key[2]));
|
||||
assert.ok(vowels.includes(key[1]));
|
||||
});
|
||||
});
|
||||
});
|
19
test/key_generators/random_spec.js
Normal file
19
test/key_generators/random_spec.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* global describe, it */
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const Generator = require('../../lib/key_generators/random');
|
||||
|
||||
describe('RandomKeyGenerator', () => {
|
||||
describe('randomKey', () => {
|
||||
it('should return a key of the proper length', () => {
|
||||
const gen = new Generator();
|
||||
assert.equal(6, gen.createKey(6).length);
|
||||
});
|
||||
|
||||
it('should use a key from the given keyset if given', () => {
|
||||
const gen = new Generator({keyspace: 'A'});
|
||||
assert.equal('AAAAAA', gen.createKey(6));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,8 +1,12 @@
|
|||
var RedisDocumentStore = require('../lib/document_stores/redis');
|
||||
/* global it, describe, afterEach */
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var winston = require('winston');
|
||||
winston.remove(winston.transports.Console);
|
||||
|
||||
var RedisDocumentStore = require('../lib/document_stores/redis');
|
||||
|
||||
describe('redis_document_store', function() {
|
||||
|
||||
/* reconnect to redis on each test */
|
||||
|
@ -12,14 +16,14 @@ describe('redis_document_store', function() {
|
|||
RedisDocumentStore.client = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
describe('set', function() {
|
||||
|
||||
it('should be able to set a key and have an expiration set', function(done) {
|
||||
var store = new RedisDocumentStore({ expire: 10 });
|
||||
store.set('hello1', 'world', function() {
|
||||
RedisDocumentStore.client.ttl('hello1', function(err, res) {
|
||||
res.should.be.above(1);
|
||||
assert.ok(res > 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -29,7 +33,7 @@ describe('redis_document_store', function() {
|
|||
var store = new RedisDocumentStore({ expire: 10 });
|
||||
store.set('hello2', 'world', function() {
|
||||
RedisDocumentStore.client.ttl('hello2', function(err, res) {
|
||||
res.should.equal(-1);
|
||||
assert.equal(-1, res);
|
||||
done();
|
||||
});
|
||||
}, true);
|
||||
|
@ -37,9 +41,9 @@ describe('redis_document_store', function() {
|
|||
|
||||
it('should not set an expiration when expiration is off', function(done) {
|
||||
var store = new RedisDocumentStore({ expire: false });
|
||||
store.set('hello3', 'world', function(worked) {
|
||||
store.set('hello3', 'world', function() {
|
||||
RedisDocumentStore.client.ttl('hello3', function(err, res) {
|
||||
res.should.equal(-1);
|
||||
assert.equal(-1, res);
|
||||
done();
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue