Compare commits
45 Commits
jsonp
...
production
Author | SHA1 | Date | |
---|---|---|---|
|
32df3370e2 | ||
|
d81195856a | ||
|
6ed427658e | ||
|
08eddc7e80 | ||
|
d040dedc6e | ||
|
5a8697cdd8 | ||
|
091ea973a8 | ||
|
939b7221ab | ||
|
934aaf7f51 | ||
|
930e21ccb7 | ||
|
eb5c8eef6a | ||
|
03dd611a86 | ||
|
f24376b192 | ||
|
eea359d0ec | ||
|
af9a71549b | ||
|
3178676fba | ||
|
3bdfab8219 | ||
|
a3a24d9765 | ||
|
8afb53e77e | ||
|
d6d9cf40f9 | ||
|
1010a142e2 | ||
|
0209375865 | ||
|
00a9d9c312 | ||
|
6835eef468 | ||
|
4626fd9c8d | ||
|
fbb6e63c37 | ||
|
84c909a5db | ||
|
45e19bc7cc | ||
|
233bc6ff16 | ||
|
360b325ced | ||
|
e93f98112b | ||
|
05cb051bc8 | ||
|
031cdd738a | ||
|
c92ab077c0 | ||
|
6c31389327 | ||
|
a8d4f3c300 | ||
|
ab029eae2f | ||
|
447d0aae76 | ||
|
4870158430 | ||
|
0471b059a0 | ||
|
5bbe50b481 | ||
|
bda2749879 | ||
|
028aa96b13 | ||
|
2deda5b68a | ||
|
ee7098457e |
42
README.md
42
README.md
@@ -46,6 +46,16 @@ STDOUT. Check the README there for more details and usages.
|
|||||||
* `storage` - storage options (see below)
|
* `storage` - storage options (see below)
|
||||||
* `logging` - logging preferences
|
* `logging` - logging preferences
|
||||||
* `keyGenerator` - key generator options (see below)
|
* `keyGenerator` - key generator options (see below)
|
||||||
|
* `rateLimits` - settings for rate limiting (see below)
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
When present, the `rateLimits` option enables built-in rate limiting courtesy
|
||||||
|
of `connect-ratelimit`. Any of the options supported by that library can be
|
||||||
|
used and set in `config.json`.
|
||||||
|
|
||||||
|
See the README for [connect-ratelimit](https://github.com/dharmafly/connect-ratelimit)
|
||||||
|
for more information!
|
||||||
|
|
||||||
## Key Generation
|
## Key Generation
|
||||||
|
|
||||||
@@ -91,7 +101,8 @@ Where `path` represents where you want the files stored
|
|||||||
|
|
||||||
### Redis
|
### Redis
|
||||||
|
|
||||||
To use redis storage you must install the redis package in npm
|
To use redis storage you must install the `redis` package in npm, and have
|
||||||
|
`redis-server` running on the machine.
|
||||||
|
|
||||||
`npm install redis`
|
`npm install redis`
|
||||||
|
|
||||||
@@ -112,6 +123,35 @@ or post.
|
|||||||
|
|
||||||
All of which are optional except `type` with very logical default values.
|
All of which are optional except `type` with very logical default values.
|
||||||
|
|
||||||
|
If your Redis server is configured for password authentification, use the `password` field.
|
||||||
|
|
||||||
|
### Postgres
|
||||||
|
|
||||||
|
To use postgres storage you must install the `pg` package in npm
|
||||||
|
|
||||||
|
`npm install pg`
|
||||||
|
|
||||||
|
Once you've done that, your config section should look like:
|
||||||
|
|
||||||
|
``` json
|
||||||
|
{
|
||||||
|
"type": "postgres",
|
||||||
|
"connectionUrl": "postgres://user:password@host:5432/database"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also just set the environment variable for `DATABASE_URL` to your database connection url.
|
||||||
|
|
||||||
|
You will have to manually add a table to your postgres database:
|
||||||
|
|
||||||
|
`create table entries (id serial primary key, key varchar(255) not null, value text not null, expiration int, unique(key));`
|
||||||
|
|
||||||
|
You can also set an `expire` option to the number of seconds to expire keys in.
|
||||||
|
This is off by default, but will constantly kick back expirations on each view
|
||||||
|
or post.
|
||||||
|
|
||||||
|
All of which are optional except `type` with very logical default values.
|
||||||
|
|
||||||
### Memcached
|
### Memcached
|
||||||
|
|
||||||
To use memcached storage you must install the `memcache` package via npm
|
To use memcached storage you must install the `memcache` package via npm
|
||||||
|
4
about.md
4
about.md
@@ -15,7 +15,7 @@ To make a new entry, click "New" (or type 'control + n')
|
|||||||
|
|
||||||
## From the Console
|
## From the Console
|
||||||
|
|
||||||
Most of the time I want to show you some text, its coming from my current
|
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
|
console session. We should make it really easy to take code from the console
|
||||||
and send it to people.
|
and send it to people.
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ pastes.
|
|||||||
|
|
||||||
## Open Source
|
## Open Source
|
||||||
|
|
||||||
Haste can easily be installed behind your network, and its all open source!
|
Haste can easily be installed behind your network, and it's all open source!
|
||||||
|
|
||||||
* [haste-client](https://github.com/seejohnrun/haste-client)
|
* [haste-client](https://github.com/seejohnrun/haste-client)
|
||||||
* [haste-server](https://github.com/seejohnrun/haste-server)
|
* [haste-server](https://github.com/seejohnrun/haste-server)
|
||||||
|
14
config.js
14
config.js
@@ -23,11 +23,17 @@
|
|||||||
"type": "phonetic"
|
"type": "phonetic"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"rateLimits": {
|
||||||
|
"categories": {
|
||||||
|
"normal": {
|
||||||
|
"totalRequests": 500,
|
||||||
|
"every": 60000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"storage": {
|
"storage": {
|
||||||
"type": "redis",
|
"type": "postgres",
|
||||||
"host": "0.0.0.0",
|
|
||||||
"port": 6379,
|
|
||||||
"db": 2,
|
|
||||||
"expire": 2592000
|
"expire": 2592000
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
|
var Busboy = require('busboy');
|
||||||
|
|
||||||
// For handling serving stored documents
|
// For handling serving stored documents
|
||||||
|
|
||||||
@@ -15,23 +16,12 @@ var DocumentHandler = function(options) {
|
|||||||
DocumentHandler.defaultKeyLength = 10;
|
DocumentHandler.defaultKeyLength = 10;
|
||||||
|
|
||||||
// Handle retrieving a document
|
// Handle retrieving a document
|
||||||
DocumentHandler.prototype.handleGet = function(key, callback, response, skipExpire) {
|
DocumentHandler.prototype.handleGet = function(key, response, skipExpire) {
|
||||||
this.store.get(key, function(ret) {
|
this.store.get(key, function(ret) {
|
||||||
if (ret) {
|
if (ret) {
|
||||||
winston.verbose('retrieved document', { key: key });
|
winston.verbose('retrieved document', { key: key });
|
||||||
var responseData = JSON.stringify({ data: ret, key: key });
|
|
||||||
if (callback) {
|
|
||||||
if (callback.match(/^[a-z0-9]+$/i)) {
|
|
||||||
response.writeHead(200, { 'content-type': 'application/javascript' });
|
|
||||||
response.end(callback + '(' + responseData + ');');
|
|
||||||
} else {
|
|
||||||
response.writeHead(400, { 'content-type': 'application/json' });
|
|
||||||
response.end(JSON.stringify({ message: 'invalid callback function name' }));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response.writeHead(200, { 'content-type': 'application/json' });
|
response.writeHead(200, { 'content-type': 'application/json' });
|
||||||
response.end(responseData);
|
response.end(JSON.stringify({ data: ret, key: key }));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
winston.warn('document not found', { key: key });
|
winston.warn('document not found', { key: key });
|
||||||
@@ -58,15 +48,14 @@ DocumentHandler.prototype.handleRawGet = function(key, response, skipExpire) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Handle adding a new Document
|
// Handle adding a new Document
|
||||||
DocumentHandler.prototype.handlePost = function(request, response) {
|
DocumentHandler.prototype.handlePost = function (request, response) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var buffer = '';
|
var buffer = '';
|
||||||
var cancelled = false;
|
var cancelled = false;
|
||||||
request.on('data', function(data) {
|
|
||||||
if (!buffer) {
|
// What to do when done
|
||||||
response.writeHead(200, { 'content-type': 'application/json' });
|
var onSuccess = function () {
|
||||||
}
|
// Check length
|
||||||
buffer += data.toString();
|
|
||||||
if (_this.maxLength && buffer.length > _this.maxLength) {
|
if (_this.maxLength && buffer.length > _this.maxLength) {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
winston.warn('document >maxLength', { maxLength: _this.maxLength });
|
winston.warn('document >maxLength', { maxLength: _this.maxLength });
|
||||||
@@ -74,14 +63,15 @@ DocumentHandler.prototype.handlePost = function(request, response) {
|
|||||||
response.end(
|
response.end(
|
||||||
JSON.stringify({ message: 'Document exceeds maximum length.' })
|
JSON.stringify({ message: 'Document exceeds maximum length.' })
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
// And then save if we should
|
||||||
request.on('end', function(end) {
|
_this.chooseKey(function (key) {
|
||||||
if (cancelled) return;
|
_this.store.set(key, buffer, function (res) {
|
||||||
_this.chooseKey(function(key) {
|
|
||||||
_this.store.set(key, buffer, function(res) {
|
|
||||||
if (res) {
|
if (res) {
|
||||||
winston.verbose('added document', { key: key });
|
var ip = request.headers['x-forwarded-for'] || request.ip;
|
||||||
|
winston.verbose('added document', { key: key, ip: ip });
|
||||||
|
response.writeHead(200, { 'content-type': 'application/json' });
|
||||||
response.end(JSON.stringify({ key: key }));
|
response.end(JSON.stringify({ key: key }));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -91,12 +81,37 @@ DocumentHandler.prototype.handlePost = function(request, response) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we should, parse a form to grab the data
|
||||||
|
var ct = request.headers['content-type'];
|
||||||
|
if (ct && ct.split(';')[0] === 'multipart/form-data') {
|
||||||
|
var busboy = new Busboy({ headers: request.headers });
|
||||||
|
busboy.on('field', function (fieldname, val) {
|
||||||
|
if (fieldname === 'data') {
|
||||||
|
buffer = val;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
request.on('error', function(error) {
|
busboy.on('finish', function () {
|
||||||
|
onSuccess();
|
||||||
|
});
|
||||||
|
request.pipe(busboy);
|
||||||
|
// Otherwise, use our own and just grab flat data from POST body
|
||||||
|
} else {
|
||||||
|
request.on('data', function (data) {
|
||||||
|
buffer += data.toString();
|
||||||
|
});
|
||||||
|
request.on('end', function () {
|
||||||
|
if (cancelled) { return; }
|
||||||
|
onSuccess();
|
||||||
|
});
|
||||||
|
request.on('error', function (error) {
|
||||||
winston.error('connection error: ' + error.message);
|
winston.error('connection error: ' + error.message);
|
||||||
response.writeHead(500, { 'content-type': 'application/json' });
|
response.writeHead(500, { 'content-type': 'application/json' });
|
||||||
response.end(JSON.stringify({ message: 'Connection error.' }));
|
response.end(JSON.stringify({ message: 'Connection error.' }));
|
||||||
|
cancelled = true;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keep choosing keys until one isn't taken
|
// Keep choosing keys until one isn't taken
|
||||||
|
79
lib/document_stores/postgres.js
Normal file
79
lib/document_stores/postgres.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*global require,module,process*/
|
||||||
|
|
||||||
|
var postgres = require('pg');
|
||||||
|
var winston = require('winston');
|
||||||
|
|
||||||
|
// create table entries (id serial primary key, key varchar(255) not null, value text not null, expiration int, unique(key));
|
||||||
|
|
||||||
|
// A postgres document store
|
||||||
|
var PostgresDocumentStore = function (options) {
|
||||||
|
this.expireJS = options.expire;
|
||||||
|
this.connectionUrl = process.env.DATABASE_URL || options.connectionUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
PostgresDocumentStore.prototype = {
|
||||||
|
|
||||||
|
// Set a given key
|
||||||
|
set: function (key, data, callback, skipExpire) {
|
||||||
|
var now = Math.floor(new Date().getTime() / 1000);
|
||||||
|
var that = this;
|
||||||
|
this.safeConnect(function (err, client, done) {
|
||||||
|
if (err) { return callback(false); }
|
||||||
|
client.query('INSERT INTO entries (key, value, expiration) VALUES ($1, $2, $3)', [
|
||||||
|
key,
|
||||||
|
data,
|
||||||
|
that.expireJS && !skipExpire ? that.expireJS + now : null
|
||||||
|
], function (err, result) {
|
||||||
|
if (err) {
|
||||||
|
winston.error('error persisting value to postgres', { error: err });
|
||||||
|
return callback(false);
|
||||||
|
}
|
||||||
|
callback(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get a given key's data
|
||||||
|
get: function (key, callback, skipExpire) {
|
||||||
|
var now = Math.floor(new Date().getTime() / 1000);
|
||||||
|
var that = this;
|
||||||
|
this.safeConnect(function (err, client, done) {
|
||||||
|
if (err) { return callback(false); }
|
||||||
|
client.query('SELECT id,value,expiration from entries where KEY = $1 and (expiration IS NULL or expiration > $2)', [key, now], function (err, result) {
|
||||||
|
if (err) {
|
||||||
|
winston.error('error retrieving value from postgres', { error: err });
|
||||||
|
return callback(false);
|
||||||
|
}
|
||||||
|
callback(result.rows.length ? result.rows[0].value : false);
|
||||||
|
if (result.rows.length && that.expireJS && !skipExpire) {
|
||||||
|
client.query('UPDATE entries SET expiration = $1 WHERE ID = $2', [
|
||||||
|
that.expireJS + now,
|
||||||
|
result.rows[0].id
|
||||||
|
], function (err, result) {
|
||||||
|
if (!err) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// A connection wrapper
|
||||||
|
safeConnect: function (callback) {
|
||||||
|
postgres.connect(this.connectionUrl, function (err, client, done) {
|
||||||
|
if (err) {
|
||||||
|
winston.error('error connecting to postgres', { error: err });
|
||||||
|
callback(err);
|
||||||
|
} else {
|
||||||
|
callback(undefined, client, done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = PostgresDocumentStore;
|
@@ -25,6 +25,10 @@ RedisDocumentStore.connect = function(options) {
|
|||||||
var port = options.port || 6379;
|
var port = options.port || 6379;
|
||||||
var index = options.db || 0;
|
var index = options.db || 0;
|
||||||
RedisDocumentStore.client = redis.createClient(port, host);
|
RedisDocumentStore.client = redis.createClient(port, host);
|
||||||
|
// authenticate if password is provided
|
||||||
|
if (options.password) {
|
||||||
|
RedisDocumentStore.client.auth(options.password);
|
||||||
|
}
|
||||||
RedisDocumentStore.client.select(index, function(err, reply) {
|
RedisDocumentStore.client.select(index, function(err, reply) {
|
||||||
if (err) {
|
if (err) {
|
||||||
winston.error(
|
winston.error(
|
||||||
|
@@ -6,13 +6,14 @@ var PhoneticKeyGenerator = function(options) {
|
|||||||
// Generate a phonetic key
|
// Generate a phonetic key
|
||||||
PhoneticKeyGenerator.prototype.createKey = function(keyLength) {
|
PhoneticKeyGenerator.prototype.createKey = function(keyLength) {
|
||||||
var text = '';
|
var text = '';
|
||||||
|
var start = Math.round(Math.random());
|
||||||
for (var i = 0; i < keyLength; i++) {
|
for (var i = 0; i < keyLength; i++) {
|
||||||
text += (i % 2 == 0) ? this.randConsonant() : this.randVowel();
|
text += (i % 2 == start) ? this.randConsonant() : this.randVowel();
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
PhoneticKeyGenerator.consonants = 'bcdfghjklmnpqrstvwxy';
|
PhoneticKeyGenerator.consonants = 'bcdfghjklmnpqrstvwxyz';
|
||||||
PhoneticKeyGenerator.vowels = 'aeiou';
|
PhoneticKeyGenerator.vowels = 'aeiou';
|
||||||
|
|
||||||
// Get an random vowel
|
// Get an random vowel
|
||||||
|
14
package.json
14
package.json
@@ -14,11 +14,14 @@
|
|||||||
},
|
},
|
||||||
"main": "haste",
|
"main": "haste",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"connect-ratelimit": "0.0.7",
|
||||||
|
"connect-route": "0.1.5",
|
||||||
|
"connect": "3.4.1",
|
||||||
|
"st": "1.1.0",
|
||||||
"winston": "0.6.2",
|
"winston": "0.6.2",
|
||||||
"connect": "1.9.2",
|
"uglify-js": "1.3.3",
|
||||||
"redis-url": "0.1.0",
|
"busboy": "0.2.4",
|
||||||
"redis": "0.8.1",
|
"pg": "4.1.1"
|
||||||
"uglify-js": "1.3.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "*",
|
"mocha": "*",
|
||||||
@@ -26,8 +29,7 @@
|
|||||||
},
|
},
|
||||||
"bundledDependencies": [],
|
"bundledDependencies": [],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "0.8.10",
|
"node": "0.10.35"
|
||||||
"npm": "1.1.49"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"haste-server": "./server.js"
|
"haste-server": "./server.js"
|
||||||
|
75
server.js
75
server.js
@@ -4,6 +4,9 @@ var fs = require('fs');
|
|||||||
|
|
||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
var connect = require('connect');
|
var connect = require('connect');
|
||||||
|
var route = require('connect-route');
|
||||||
|
var connect_st = require('st');
|
||||||
|
var connect_rate_limit = require('connect-ratelimit');
|
||||||
|
|
||||||
var DocumentHandler = require('./lib/document_handler');
|
var DocumentHandler = require('./lib/document_handler');
|
||||||
|
|
||||||
@@ -37,7 +40,7 @@ if (!config.storage.type) {
|
|||||||
|
|
||||||
var Store, preferredStore;
|
var Store, preferredStore;
|
||||||
|
|
||||||
if (process.env.REDISTOGO_URL) {
|
if (process.env.REDISTOGO_URL && config.storage.type === 'redis') {
|
||||||
var redisClient = require('redis-url').connect(process.env.REDISTOGO_URL);
|
var redisClient = require('redis-url').connect(process.env.REDISTOGO_URL);
|
||||||
Store = require('./lib/document_stores/redis');
|
Store = require('./lib/document_stores/redis');
|
||||||
preferredStore = new Store(config.storage, redisClient);
|
preferredStore = new Store(config.storage, redisClient);
|
||||||
@@ -99,44 +102,58 @@ var documentHandler = new DocumentHandler({
|
|||||||
keyGenerator: keyGenerator
|
keyGenerator: keyGenerator
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the server up with a static cache
|
var app = connect();
|
||||||
connect.createServer(
|
|
||||||
// First look for api calls
|
// Rate limit all requests
|
||||||
connect.router(function(app) {
|
if (config.rateLimits) {
|
||||||
|
config.rateLimits.end = true;
|
||||||
|
app.use(connect_rate_limit(config.rateLimits));
|
||||||
|
}
|
||||||
|
|
||||||
|
// first look at API calls
|
||||||
|
app.use(route(function(router) {
|
||||||
// get raw documents - support getting with extension
|
// get raw documents - support getting with extension
|
||||||
app.get('/raw/:id', function(request, response, next) {
|
router.get('/raw/:id', function(request, response, next) {
|
||||||
var skipExpire = !!config.documents[request.params.id];
|
|
||||||
var key = request.params.id.split('.')[0];
|
var key = request.params.id.split('.')[0];
|
||||||
|
var skipExpire = !!config.documents[key];
|
||||||
return documentHandler.handleRawGet(key, response, skipExpire);
|
return documentHandler.handleRawGet(key, response, skipExpire);
|
||||||
});
|
});
|
||||||
// add documents
|
// add documents
|
||||||
app.post('/documents', function(request, response, next) {
|
router.post('/documents', function(request, response, next) {
|
||||||
return documentHandler.handlePost(request, response);
|
return documentHandler.handlePost(request, response);
|
||||||
});
|
});
|
||||||
// get documents
|
// get documents
|
||||||
app.get('/documents/:id', function(request, response, next) {
|
router.get('/documents/:id', function(request, response, next) {
|
||||||
var skipExpire = !!config.documents[request.params.id];
|
var key = request.params.id.split('.')[0];
|
||||||
var parsedUrl = url.parse(request.url, true);
|
var skipExpire = !!config.documents[key];
|
||||||
return documentHandler.handleGet(
|
return documentHandler.handleGet(key, response, skipExpire);
|
||||||
request.params.id,
|
|
||||||
parsedUrl.query.callback,
|
|
||||||
response,
|
|
||||||
skipExpire
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}),
|
}));
|
||||||
// Otherwise, static
|
|
||||||
connect.staticCache(),
|
// Otherwise, try to match static files
|
||||||
connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }),
|
app.use(connect_st({
|
||||||
// Then we can loop back - and everything else should be a token,
|
path: __dirname + '/static',
|
||||||
// so route it back to /index.html
|
content: { maxAge: config.staticMaxAge },
|
||||||
connect.router(function(app) {
|
passthrough: true,
|
||||||
app.get('/:id', function(request, response, next) {
|
index: false
|
||||||
request.url = request.originalUrl = '/index.html';
|
}));
|
||||||
|
|
||||||
|
// Then we can loop back - and everything else should be a token,
|
||||||
|
// so route it back to /
|
||||||
|
app.use(route(function(router) {
|
||||||
|
router.get('/:id', function(request, response, next) {
|
||||||
|
request.sturl = '/';
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}),
|
}));
|
||||||
connect.static(__dirname + '/static', { maxAge: config.staticMaxAge })
|
|
||||||
).listen(config.port, config.host);
|
// And match index
|
||||||
|
app.use(connect_st({
|
||||||
|
path: __dirname + '/static',
|
||||||
|
content: { maxAge: config.staticMaxAge },
|
||||||
|
index: 'index.html'
|
||||||
|
}));
|
||||||
|
|
||||||
|
http.createServer(app).listen(config.port, config.host);
|
||||||
|
|
||||||
winston.info('listening on ' + config.host + ':' + config.port);
|
winston.info('listening on ' + config.host + ':' + config.port);
|
||||||
|
@@ -43,6 +43,7 @@ textarea {
|
|||||||
outline: none;
|
outline: none;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding-right: 360px;
|
padding-right: 360px;
|
||||||
|
overflow: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#box code {
|
#box code {
|
||||||
@@ -148,6 +149,7 @@ textarea {
|
|||||||
#box2 .function.twitter { background-position: -153px top; }
|
#box2 .function.twitter { background-position: -153px top; }
|
||||||
#box2 .function.enabled.twitter { background-position: -153px center; }
|
#box2 .function.enabled.twitter { background-position: -153px center; }
|
||||||
#box2 .function.enabled.twitter:hover { background-position: -153px bottom; }
|
#box2 .function.enabled.twitter:hover { background-position: -153px bottom; }
|
||||||
|
#box2 .button-picture{ border-width: 0; font-size: inherit; }
|
||||||
|
|
||||||
#messages {
|
#messages {
|
||||||
position:fixed;
|
position:fixed;
|
||||||
@@ -167,3 +169,4 @@ textarea {
|
|||||||
#messages li.error {
|
#messages li.error {
|
||||||
background:rgba(102,8,0,0.8);
|
background:rgba(102,8,0,0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -166,9 +166,10 @@ haste.extensionMap = {
|
|||||||
rb: 'ruby', py: 'python', pl: 'perl', php: 'php', scala: 'scala', go: 'go',
|
rb: 'ruby', py: 'python', pl: 'perl', php: 'php', scala: 'scala', go: 'go',
|
||||||
xml: 'xml', html: 'xml', htm: 'xml', css: 'css', js: 'javascript', vbs: 'vbscript',
|
xml: 'xml', html: 'xml', htm: 'xml', css: 'css', js: 'javascript', vbs: 'vbscript',
|
||||||
lua: 'lua', pas: 'delphi', java: 'java', cpp: 'cpp', cc: 'cpp', m: 'objectivec',
|
lua: 'lua', pas: 'delphi', java: 'java', cpp: 'cpp', cc: 'cpp', m: 'objectivec',
|
||||||
vala: 'vala', cs: 'cs', sql: 'sql', sm: 'smalltalk', lisp: 'lisp', ini: 'ini',
|
vala: 'vala', sql: 'sql', sm: 'smalltalk', lisp: 'lisp', ini: 'ini',
|
||||||
diff: 'diff', bash: 'bash', sh: 'bash', tex: 'tex', erl: 'erlang', hs: 'haskell',
|
diff: 'diff', bash: 'bash', sh: 'bash', tex: 'tex', erl: 'erlang', hs: 'haskell',
|
||||||
md: 'markdown', txt: '', coffee: 'coffee', json: 'javascript'
|
md: 'markdown', txt: '', coffee: 'coffee', json: 'javascript',
|
||||||
|
swift: 'swift'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Look up the extension preferred for a type
|
// Look up the extension preferred for a type
|
||||||
|
2
static/application.min.js
vendored
2
static/application.min.js
vendored
File diff suppressed because one or more lines are too long
3
static/highlight.min.js
vendored
3
static/highlight.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
|
|
||||||
<title>hastebin</title>
|
<title>hastebin</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
<link rel="stylesheet" type="text/css" href="solarized_dark.css"/>
|
<link rel="stylesheet" type="text/css" href="solarized_dark.css"/>
|
||||||
<link rel="stylesheet" type="text/css" href="application.css"/>
|
<link rel="stylesheet" type="text/css" href="application.css"/>
|
||||||
|
|
||||||
@@ -47,11 +47,11 @@
|
|||||||
<a href="/about.md" class="logo"></a>
|
<a href="/about.md" class="logo"></a>
|
||||||
</div>
|
</div>
|
||||||
<div id="box2">
|
<div id="box2">
|
||||||
<div class="save function"></div>
|
<button class="save function button-picture">Save</button>
|
||||||
<div class="new function"></div>
|
<button class="new function button-picture">New</button>
|
||||||
<div class="duplicate function"></div>
|
<button class="duplicate function button-picture">Duplicate & Edit</button>
|
||||||
<div class="raw function"></div>
|
<button class="raw function button-picture">Just Text</button>
|
||||||
<div class="twitter function"></div>
|
<button class="twitter function button-picture">Twitter</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="box3" style="display:none;">
|
<div id="box3" style="display:none;">
|
||||||
<div class="label"></div>
|
<div class="label"></div>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="linenos"></div>
|
<div id="linenos"></div>
|
||||||
<pre id="box" style="display:none;" tabindex="0"><code></code></pre>
|
<pre id="box" style="display:none;" class="hljs" tabindex="0"><code></code></pre>
|
||||||
<textarea spellcheck="false" style="display:none;"></textarea>
|
<textarea spellcheck="false" style="display:none;"></textarea>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@@ -4,97 +4,81 @@ Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmai
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pre code {
|
.hljs {
|
||||||
display: block; padding: 0.5em;
|
display: block;
|
||||||
background: #002b36; color: #92a0a0;
|
overflow-x: auto;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #002b36;
|
||||||
|
color: #839496;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre .comment,
|
.hljs-comment,
|
||||||
pre .template_comment,
|
.hljs-quote {
|
||||||
pre .diff .header,
|
|
||||||
pre .doctype,
|
|
||||||
pre .lisp .string,
|
|
||||||
pre .javadoc {
|
|
||||||
color: #586e75;
|
color: #586e75;
|
||||||
font-style: italic;
|
|
||||||
display: inline-block;
|
|
||||||
line-height: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre .keyword,
|
/* Solarized Green */
|
||||||
pre .css .rule .keyword,
|
.hljs-keyword,
|
||||||
pre .winutils,
|
.hljs-selector-tag,
|
||||||
pre .javascript .title,
|
.hljs-addition {
|
||||||
pre .method,
|
|
||||||
pre .addition,
|
|
||||||
pre .css .tag,
|
|
||||||
pre .lisp .title {
|
|
||||||
color: #859900;
|
color: #859900;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre .number,
|
/* Solarized Cyan */
|
||||||
pre .command,
|
.hljs-number,
|
||||||
pre .string,
|
.hljs-string,
|
||||||
pre .tag .value,
|
.hljs-meta .hljs-meta-string,
|
||||||
pre .phpdoc,
|
.hljs-literal,
|
||||||
pre .tex .formula,
|
.hljs-doctag,
|
||||||
pre .regexp,
|
.hljs-regexp {
|
||||||
pre .hexcolor {
|
|
||||||
color: #2aa198;
|
color: #2aa198;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre .title,
|
/* Solarized Blue */
|
||||||
pre .localvars,
|
.hljs-title,
|
||||||
pre .function .title,
|
.hljs-section,
|
||||||
pre .chunk,
|
.hljs-name,
|
||||||
pre .decorator,
|
.hljs-selector-id,
|
||||||
pre .builtin,
|
.hljs-selector-class {
|
||||||
pre .built_in,
|
|
||||||
pre .lisp .title,
|
|
||||||
pre .identifier,
|
|
||||||
pre .title .keymethods,
|
|
||||||
pre .id,
|
|
||||||
pre .header {
|
|
||||||
color: #268bd2;
|
color: #268bd2;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre .tag .title,
|
/* Solarized Yellow */
|
||||||
pre .rules .property,
|
.hljs-attribute,
|
||||||
pre .django .tag .keyword {
|
.hljs-attr,
|
||||||
font-weight: bold;
|
.hljs-variable,
|
||||||
}
|
.hljs-template-variable,
|
||||||
|
.hljs-class .hljs-title,
|
||||||
pre .attribute,
|
.hljs-type {
|
||||||
pre .variable,
|
|
||||||
pre .instancevar,
|
|
||||||
pre .lisp .body,
|
|
||||||
pre .smalltalk .number,
|
|
||||||
pre .constant,
|
|
||||||
pre .class .title,
|
|
||||||
pre .parent,
|
|
||||||
pre .haskell .label {
|
|
||||||
color: #b58900;
|
color: #b58900;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre .preprocessor,
|
/* Solarized Orange */
|
||||||
pre .pi,
|
.hljs-symbol,
|
||||||
pre .shebang,
|
.hljs-bullet,
|
||||||
pre .symbol,
|
.hljs-subst,
|
||||||
pre .diff .change,
|
.hljs-meta,
|
||||||
pre .special,
|
.hljs-meta .hljs-keyword,
|
||||||
pre .keymethods,
|
.hljs-selector-attr,
|
||||||
pre .attr_selector,
|
.hljs-selector-pseudo,
|
||||||
pre .important,
|
.hljs-link {
|
||||||
pre .subst,
|
|
||||||
pre .cdata {
|
|
||||||
color: #cb4b16;
|
color: #cb4b16;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre .deletion {
|
/* Solarized Red */
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-deletion {
|
||||||
color: #dc322f;
|
color: #dc322f;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre .tex .formula,
|
.hljs-formula {
|
||||||
pre .code {
|
|
||||||
background: #073642;
|
background: #073642;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user