102 Commits

Author SHA1 Message Date
John Crepezzi
32df3370e2 Fix header name 2017-05-02 20:08:03 -04:00
John Crepezzi
d81195856a Log ip 2017-05-02 20:08:03 -04:00
John Crepezzi
6ed427658e Remove npm 2017-05-02 20:08:03 -04:00
John Crepezzi
08eddc7e80 Added pg 2017-05-02 20:08:03 -04:00
John Crepezzi
d040dedc6e Production uses postgres 2017-05-02 20:07:40 -04:00
John Crepezzi
5a8697cdd8 Fix typo in about.md 2017-05-02 17:32:34 -04:00
John Crepezzi
091ea973a8 Merge pull request #138 from Wohlstand/patch-2
Added a note about Redis's password field
2017-04-21 07:51:23 -04:00
John Crepezzi
939b7221ab Merge pull request #146 from seejohnrun/dont_expire_static_documents
Don't expire static documents on raw read
2017-01-28 11:58:12 -05:00
John Crepezzi
934aaf7f51 Don't expire static documents on raw read 2017-01-28 11:57:25 -05:00
Vitaly Novichkov
930e21ccb7 Added a note about Redis's password field 2016-11-06 02:25:51 +04:00
John Crepezzi
eb5c8eef6a Merge pull request #135 from seejohnrun/horizontal_scroll_fix
Fix horizontal scroll overflow
2016-09-30 10:40:15 -05:00
John Crepezzi
03dd611a86 Fix horizontal scroll overflow
Caused by highlight.js upgrade
2016-09-30 09:57:46 -05:00
John Crepezzi
f24376b192 Merge pull request #129 from Wohlstand/patch-1
Fix working error under Firefox
2016-09-19 09:36:56 -04:00
Vitaly Novichkov
eea359d0ec Update index.html
Short charset command better for HTML5
2016-09-13 15:11:20 +04:00
John Crepezzi
af9a71549b Upgrade highlight.js
Also adds swift support

Closes #69
2016-09-12 20:10:15 -04:00
John Crepezzi
3178676fba Merge pull request #131 from seejohnrun/update_doc
Update documentation to mention redis-server
2016-08-22 13:23:11 -04:00
John Crepezzi
3bdfab8219 Update documentation to mention redis-server 2016-08-22 13:22:49 -04:00
John Crepezzi
a3a24d9765 Merge pull request #127 from Gwemox/master
Compatibility with screen reader
2016-08-17 14:03:13 -04:00
Vitaly Novichkov
8afb53e77e Fix working error under Firefox
On some computers because undeclared charset, highlight.min.js parsing incorrectly while some charsets (For example, Cyrillic-1251) are toggled. Problem is going away when I manually toggling Europan or UTF-8 charset.
2016-08-03 14:15:58 +04:00
Thibault Buathier
d6d9cf40f9 Compatibility with screen reader 2016-07-30 16:15:53 +02:00
Thibault Buathier
1010a142e2 Compatibility with screen reader 2016-07-30 16:15:26 +02:00
John Crepezzi
0209375865 Revert "Added cloudron external provider"
This reverts commit 00a9d9c312.
2016-03-12 13:09:28 -05:00
John Crepezzi
00a9d9c312 Added cloudron external provider 2016-03-12 13:07:58 -05:00
John Crepezzi
6835eef468 Merge pull request #109 from seejohnrun/rate_limiting
Added user-configurable rate limiting
2016-03-10 11:44:57 -10:00
John Crepezzi
4626fd9c8d Merge pull request #81 from pangeacake/master
Add postgres information to README.md
2016-03-06 19:06:04 -05:00
John Crepezzi
fbb6e63c37 Added a note to the README 2016-03-06 16:34:33 -05:00
John Crepezzi
84c909a5db Added user-configurable rate limiting 2016-03-06 16:20:40 -05:00
John Crepezzi
45e19bc7cc fix indentation 2015-12-27 12:59:59 -05:00
John Crepezzi
233bc6ff16 Merge pull request #77 from abn/redis-auth
Support authentication for redis store if password provided
2015-12-27 12:58:24 -05:00
PangeaCake
360b325ced Smaller typo 2015-01-07 14:30:12 -08:00
PangeaCake
e93f98112b Add pg as dependency and update node version
One of the dependencies seemed to be broken with the previous node version, but this node version worked perfectly
2015-01-07 14:27:46 -08:00
PangeaCake
05cb051bc8 Tiny fix 2015-01-07 14:25:43 -08:00
PangeaCake
031cdd738a Add postgres information to README.md 2015-01-07 14:24:50 -08:00
Arun Babu Neelicattu
c92ab077c0 Support authentication for redis store if password provided 2014-11-21 23:17:19 +10:00
John Crepezzi
6c31389327 Merge pull request #64 from lidl/master
Make table creation comment a one-liner.
2014-07-16 07:54:00 -04:00
lidl
a8d4f3c300 Make table creation comment a one-liner. 2014-06-27 18:40:05 -04:00
John Crepezzi
ab029eae2f Added postgres adapter 2014-06-09 16:50:43 -04:00
John Crepezzi
447d0aae76 PG basis 2014-06-09 14:48:35 -04:00
John Crepezzi
4870158430 Merge branch 'master' of github.com:seejohnrun/haste-server 2014-04-21 14:17:09 -04:00
John Crepezzi
0471b059a0 Support a form-data POST API
Closes #54
2014-04-21 14:16:23 -04:00
John Crepezzi
5bbe50b481 Merge pull request #59 from jomo/phonetic
Phonetic key improvements
2014-04-15 11:00:49 -04:00
JonApps
bda2749879 oops 🍺 2014-03-25 02:23:31 +01:00
JonApps
028aa96b13 phonetic keys can begin with vowel + added missing \'z\' to consontants 2014-03-25 02:20:05 +01:00
John Crepezzi
2deda5b68a Merge pull request #56 from joeykrim/patch-1
Small typo
2014-02-27 19:26:31 -05:00
joeykrim
ee7098457e Small typo
Changed "its all open source" to "it's all open source"
2014-02-27 17:57:37 -05:00
John Crepezzi
7a08960414 Merge branch 'master' of github.com:seejohnrun/haste-server 2013-11-24 11:54:34 -05:00
John Crepezzi
89909747f1 Don't depend on err.message for redis errors [#49] 2013-11-24 11:54:01 -05:00
John Crepezzi
202e695e07 Remove GA from index.html on Master 2013-10-31 08:44:33 -04:00
John Crepezzi
48e8e79659 Remove support from README 2013-08-13 13:06:37 -04:00
John Crepezzi
abb49f2cf3 update about.md 2013-03-12 21:59:10 -04:00
John Crepezzi
d1cd2a5213 Proper 2013-01-11 10:00:35 -08:00
John Crepezzi
27317844e0 Remove nl 2013-01-11 09:57:10 -08:00
John Crepezzi
ee74e2fa90 Merge branch 'production' of github.com:seejohnrun/haste-server into production 2012-12-29 18:15:45 -05:00
John Crepezzi
5d8bd2e6f8 Merge branch 'master' into production 2012-12-29 18:15:32 -05:00
John Crepezzi
cd4c7aeab8 Merge pull request #37 from naftis/patch-1
Bugfix to solarized_dark.css
2012-12-28 07:10:36 -08:00
naftis
e37c3cf1b9 Bugfix to solarized_dark.css
Fixed bug with font-style: italic and WebKit.
WebKit makes line-height bigger, when italic is used.
2012-12-28 16:11:21 +02:00
John Crepezzi
8858bab985 Merge branch 'master' into production 2012-12-23 10:54:15 -05:00
John Crepezzi
afb0c332cc Added shift modifier to twitter shortcut
Closes #29
2012-12-23 10:53:53 -05:00
John Crepezzi
82c58c5c0c Merge branch 'master' into production 2012-12-19 08:18:14 -05:00
John Crepezzi
46bdd27431 Fix for type name ;)
Closes #28
2012-12-19 08:17:52 -05:00
John Crepezzi
1adfba1a37 Merge branch 'master' into production 2012-12-19 08:13:36 -05:00
John Crepezzi
54e55b1b0d Added JSON to extension map (JS)
Closes #28
2012-12-19 08:12:08 -05:00
John Crepezzi
08d37cc7f7 Added support section 2012-10-22 14:40:52 -04:00
John Crepezzi
aa781957e8 Update Copyright 2012-09-27 13:46:27 -04:00
John Crepezzi
c00477c93c Update Copyright 2012-09-27 13:46:09 -04:00
John Crepezzi
035f09ac05 GA 2012-09-27 13:43:53 -04:00
John Crepezzi
36e00bb29e Remove 'localhost' references 2012-09-27 12:03:52 -04:00
John Crepezzi
10623873e8 Allow host setting by ENV 2012-09-27 12:01:00 -04:00
John Crepezzi
e536ba1019 Move to an available npm version 2012-09-27 11:56:49 -04:00
John Crepezzi
85fc36d710 Update npm version 2012-09-27 11:56:15 -04:00
John Crepezzi
5d5ae164f3 Set up node engine version 2012-09-27 11:54:40 -04:00
John Crepezzi
79309c75df Bump version to 0.1.0 2012-09-27 11:51:15 -04:00
John Crepezzi
4b58c8d356 Added more loggin 2012-09-27 11:50:56 -04:00
John Crepezzi
8f0d6260b0 change how redistogo install works 2012-09-27 11:50:12 -04:00
John Crepezzi
93a83a35da Logging 2012-09-27 11:47:23 -04:00
John Crepezzi
4efc5d47d9 Allow redistogo 2012-09-27 11:46:53 -04:00
John Crepezzi
ff8ef54e34 Procfile 2012-09-27 11:38:14 -04:00
John Crepezzi
814a49812a Update server config path 2012-09-19 14:28:52 -04:00
John Crepezzi
e0610bc1be Fix multiple document loading
Closes #32
2012-08-13 11:33:20 -04:00
John Crepezzi
962976c204 Pad the right 2012-06-22 15:33:07 -04:00
John Crepezzi
16080bdc16 Update description - preparing for npm push 2012-04-21 23:49:39 -04:00
John Crepezzi
20ce741341 Fix indentation 2012-04-07 23:51:48 -04:00
John Crepezzi
13bb094fb3 Revert "Refactor frontend"
This reverts commit 1950cc8db0.
2012-03-19 18:17:39 -04:00
John Crepezzi
b43a55ffda Merge pull request #25 from zaeleus/backbone
Refactor frontend
2012-03-19 15:11:08 -07:00
John Crepezzi
45cbdcce70 Force down connect version 2012-03-02 14:07:59 -05:00
Michael Macias
1950cc8db0 Refactor frontend
* restructured JavaScript using backbone.js
* replaced highlight.js with CodeMirror for its editor
* added CodeMirror Solarized (dark) theme based on Ethan Schoonover's solarized.vim
* changed `POST /document` to accept real JSON
* cleaned up template and stylesheet
2012-02-18 02:40:56 -06:00
John Crepezzi
90cfe0ec57 Upgrade jquery to 1.7.1 2012-02-07 17:52:48 -05:00
John Crepezzi
87e28548b9 Explicitly set encoding
Closes #24
2012-02-07 17:52:31 -05:00
John Crepezzi
ca9d4c18f7 Added a note on WinHaste to about.md 2012-01-27 09:47:56 -05:00
John Crepezzi
f147c05ff1 Add more detail to README.md 2012-01-27 09:46:24 -05:00
John Crepezzi
7a2851aeeb Merge branch 'master' of github.com:seejohnrun/haste-server 2012-01-24 22:29:38 -05:00
John Crepezzi
ef96704f2c Basic coffeescript highlighting
From https://github.com/dnagir/highlightjs-coffeescript

Closes #2
2012-01-24 22:28:58 -05:00
John Crepezzi
8b7ac341e7 FileDocumentStore fix
Closes #20
2012-01-24 00:01:38 -05:00
John Crepezzi
713914aecd Merge pull request #18 from tfausak/patch-1
Fix typo in about.md ("easist" -> "easiest").
2012-01-23 19:27:24 -08:00
Taylor Fausak
36e7a43c14 Fix typo in about.md ("easist" -> "easiest"). 2012-01-23 20:12:33 -06:00
John Crepezzi
047a686248 Open raw in same window 2012-01-23 11:40:09 -05:00
John Crepezzi
347ba57ad3 Merge branch 'raw_icon' 2012-01-23 11:38:36 -05:00
Brian Dawson
d2bd78956b Merge branch 'master' of github.com:seejohnrun/haste-server
Conflicts:
	static/function-icons.png
2012-01-22 23:42:41 -05:00
Brian Dawson
1fe81d2447 update function icons 2012-01-22 23:41:40 -05:00
John Crepezzi
f37d1ad401 Clean up some code and move document stores into subfolder 2012-01-21 15:19:55 -05:00
John Crepezzi
483fce891d Added memcached support 2012-01-17 14:43:33 -05:00
John Crepezzi
6e4c087319 Remove hashlib dependency and switch to mocha for testing 2012-01-13 11:17:15 -05:00
23 changed files with 588 additions and 317 deletions

1
Procfile Normal file
View File

@@ -0,0 +1 @@
web: node server.js

128
README.md
View File

@@ -1,6 +1,9 @@
# Haste # Haste
Haste is an open-source pastebin software written in node.js, which is easily installable in any network. It can be backed by either redis or filesystem, and has a very easy adapter interface for other stores. A publicly available version can be found at [hastebin.com](http://hastebin.com) Haste is an open-source pastebin software written in node.js, which is easily
installable in any network. It can be backed by either redis or filesystem,
and has a very easy adapter interface for other stores. A publicly available
version can be found at [hastebin.com](http://hastebin.com)
Major design objectives: Major design objectives:
@@ -8,11 +11,14 @@ Major design objectives:
* Be really simple * Be really simple
* Be easy to set up and use * Be easy to set up and use
Haste works really well with a little utility called haste-client, allowing you to do things like: Haste works really well with a little utility called
[haste-client](https://github.com/seejohnrun/haste-client), allowing you
to do things like:
`cat something | haste` `cat something | haste`
which will output a URL to share containing the contents of `cat something`'s STDOUT which will output a URL to share containing the contents of `cat something`'s
STDOUT. Check the README there for more details and usages.
## Tested Browsers ## Tested Browsers
@@ -35,10 +41,21 @@ which will output a URL to share containing the contents of `cat something`'s ST
* `maxLength` - maximum length of a paste (default none) * `maxLength` - maximum length of a paste (default none)
* `staticMaxAge` - max age for static assets (86400) * `staticMaxAge` - max age for static assets (86400)
* `recompressStatisAssets` - whether or not to compile static js assets (true) * `recompressStatisAssets` - 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. * `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) * `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
@@ -48,7 +65,7 @@ Attempts to generate phonetic keys, similar to `pwgen`
``` json ``` json
{ {
"type": "phonetic" "type": "phonetic"
} }
``` ```
@@ -58,23 +75,25 @@ Generates a random key
``` json ``` json
{ {
"type": "random", "type": "random",
"keyspace": "abcdef" "keyspace": "abcdef"
} }
``` ```
The _optional_ keySpace argument is a string of acceptable characters for the key. The _optional_ keySpace argument is a string of acceptable characters
for the key.
## Storage ## Storage
### File ### File
To use file storage (the default) change the storage section in `config.js` to something like: To use file storage (the default) change the storage section in `config.js` to
something like:
``` json ``` json
{ {
"path": "./data", "path": "./data",
"type": "file" "type": "file"
} }
``` ```
@@ -82,22 +101,76 @@ Where `path` represents where you want the files stored
### Redis ### Redis
To use redis storage you must install the redis package in npm globall using To use redis storage you must install the `redis` package in npm, and have
`redis-server` running on the machine.
`npm install redis --global` `npm install redis`
Once you've done that, your config section should look like: Once you've done that, your config section should look like:
``` json ``` json
{ {
"type": "redis", "type": "redis",
"host": "localhost", "host": "localhost",
"port": 6379, "port": 6379,
"db": 2 "db": 2
} }
``` ```
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. 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.
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
To use memcached storage you must install the `memcache` package via npm
`npm install memcache`
Once you've done that, your config section should look like:
``` json
{
"type": "memcached",
"host": "127.0.0.1",
"port": 11211
}
```
You can also set an `expire` option to the number of seconds to expire keys in.
This behaves just like the redis expirations, but does not push expirations
forward on GETs.
All of which are optional except `type` with very logical default values. All of which are optional except `type` with very logical default values.
@@ -109,15 +182,28 @@ John Crepezzi <john.crepezzi@gmail.com>
(The MIT License) (The MIT License)
Copyright © 2011 John Crepezzi Copyright © 2011-2012 John Crepezzi
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the Software), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
### Other components: ### Other components:
* jQuery: MIT/GPL license * jQuery: MIT/GPL license
* highlight.js: Copyright © 2006, Ivan Sagalaev * highlight.js: Copyright © 2006, Ivan Sagalaev
* highlightjs-coffeescript: WTFPL - Copyright © 2011, Dmytrii Nagirniak

View File

@@ -1 +0,0 @@
* add feedback for errors to UI - esp. too long

View File

@@ -1,49 +1,56 @@
# Haste # Haste
Sharing code is a good thing, and it should be _really_ easy to do it. Sharing code is a good thing, and it should be _really_ easy to do it.
A lot of times, I want to show you something I'm seeing - and that's where we use pastebins. A lot of times, I want to show you something I'm seeing - and that's where we
use pastebins.
Haste is the prettiest, easist to use pastebin ever made. Haste is the prettiest, easiest to use pastebin ever made.
## Basic Usage ## Basic Usage
Type what you want me to see, click "Save", and then copy the URL. Send that URL Type what you want me to see, click "Save", and then copy the URL. Send that
to someone and they'll see what you see. URL to someone and they'll see what you see.
To make a new entry, click "New" (or type 'control + n') 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 console session. Most of the time I want to show you some text, it's coming from my current
We should make it really easy to take code from the console and send it to people. 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` # http://hastebin.com/1238193
You can even take this a step further, and cut out the last step of copying the URL with: You can even take this a step further, and cut out the last step of copying the
URL with:
* osx: `cat something | haste | pbcopy` * osx: `cat something | haste | pbcopy`
* linux: `cat something | haste | xsel` * linux: `cat something | haste | xsel`
* windows: check out [WinHaste](https://github.com/ajryan/WinHaste)
After running that, the STDOUT output of `cat something` will show up at a URL which has After running that, the STDOUT output of `cat something` will show up at a URL
been conveniently copied to your clipboard. which has been conveniently copied to your clipboard.
That's all there is to that, and you can install it with `gem install haste` right now. That's all there is to that, and you can install it with `gem install haste`
right now.
* osx: you will need to have an up to date version of Xcode * osx: you will need to have an up to date version of Xcode
* linux: you will need to have rubygems and ruby-devel installed * linux: you will need to have rubygems and ruby-devel installed
## Duration ## Duration
Pastes will stay for 30 days from their last view. Pastes will stay for 30 days from their last view. They may be removed earlier
and without notice.
## Privacy ## Privacy
While the contents of hastebin.com are not directly crawled by any search robot that While the contents of hastebin.com are not directly crawled by any search robot
obeys "robots.txt", there should be no great expectation of privacy. Post things at your that obeys "robots.txt", there should be no great expectation of privacy. Post
own risk. Not responsible for any loss of data or removed pastes. things at your own risk. Not responsible for any loss of data or removed
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)

View File

@@ -1,6 +1,6 @@
{ {
"host": "localhost", "host": "0.0.0.0",
"port": 7777, "port": 7777,
"keyLength": 10, "keyLength": 10,
@@ -23,11 +23,17 @@
"type": "phonetic" "type": "phonetic"
}, },
"rateLimits": {
"categories": {
"normal": {
"totalRequests": 500,
"every": 60000
}
}
},
"storage": { "storage": {
"type": "redis", "type": "postgres",
"host": "localhost",
"port": 6379,
"db": 2,
"expire": 2592000 "expire": 2592000
}, },

View File

@@ -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
@@ -47,28 +48,30 @@ 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 });
response.writeHead(400, { 'content-type': 'application/json' }); response.writeHead(400, { 'content-type': 'application/json' });
response.end(JSON.stringify({ message: 'Document exceeds maximum length.' })); response.end(
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 {
@@ -78,17 +81,42 @@ DocumentHandler.prototype.handlePost = function(request, response) {
} }
}); });
}); });
}); };
request.on('error', function(error) {
winston.error('connection error: ' + error.message); // If we should, parse a form to grab the data
response.writeHead(500, { 'content-type': 'application/json' }); var ct = request.headers['content-type'];
response.end(JSON.stringify({ message: 'Connection error.' })); 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;
}
});
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);
response.writeHead(500, { 'content-type': 'application/json' });
response.end(JSON.stringify({ message: 'Connection error.' }));
cancelled = true;
});
}
}; };
// Get a random key that hasn't been already used // Keep choosing keys until one isn't taken
DocumentHandler.prototype.chooseKey = function(callback) { DocumentHandler.prototype.chooseKey = function(callback) {
var key = this.keyGenerator.createKey(this.keyLength); var key = this.acceptableKey();
var _this = this; var _this = this;
this.store.get(key, function(ret) { this.store.get(key, function(ret) {
if (ret) { if (ret) {
@@ -99,4 +127,8 @@ DocumentHandler.prototype.chooseKey = function(callback) {
}); });
}; };
DocumentHandler.prototype.acceptableKey = function() {
return this.keyGenerator.createKey(this.keyLength);
};
module.exports = DocumentHandler; module.exports = DocumentHandler;

View File

@@ -1,7 +1,7 @@
var fs = require('fs'); var fs = require('fs');
var crypto = require('crypto');
var winston = require('winston'); var winston = require('winston');
var hashlib = require('hashlib');
// For storing in files // For storing in files
// options[type] = file // options[type] = file
@@ -12,12 +12,21 @@ var FileDocumentStore = function(options) {
this.expire = options.expire; this.expire = options.expire;
}; };
// Save data in a file, key as md5 - since we don't know what we could be passed here // Generate md5 of a string
FileDocumentStore.md5 = function(str) {
var md5sum = crypto.createHash('md5');
md5sum.update(str);
return md5sum.digest('hex');
};
// Save data in a file, key as md5 - since we don't know what we could
// be passed here
FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) { FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
try { try {
var _this = this; var _this = this;
fs.mkdir(this.basePath, '700', function() { fs.mkdir(this.basePath, '700', function() {
fs.writeFile(_this.basePath + '/' + hashlib.md5(key), data, 'utf8', function(err) { var fn = _this.basePath + '/' + FileDocumentStore.md5(key);
fs.writeFile(fn, data, 'utf8', function(err) {
if (err) { if (err) {
callback(false); callback(false);
} }
@@ -37,7 +46,8 @@ FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
// Get data from a file from key // Get data from a file from key
FileDocumentStore.prototype.get = function(key, callback, skipExpire) { FileDocumentStore.prototype.get = function(key, callback, skipExpire) {
var _this = this; var _this = this;
fs.readFile(this.basePath + '/' + hashlib.md5(key), 'utf8', function(err, data) { var fn = this.basePath + '/' + FileDocumentStore.md5(key);
fs.readFile(fn, 'utf8', function(err, data) {
if (err) { if (err) {
callback(false); callback(false);
} }

View File

@@ -0,0 +1,45 @@
var memcached = require('memcache');
var winston = require('winston');
// Create a new store with options
var MemcachedDocumentStore = function(options) {
this.expire = options.expire;
if (!MemcachedDocumentStore.client) {
MemcachedDocumentStore.connect(options);
}
};
// 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 });
});
};
// 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);
};
// 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;

View 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;

View File

@@ -1,6 +1,5 @@
var redis = require('redis'); var redis = require('redis');
var winston = require('winston'); var winston = require('winston');
var hashlib = require('hashlib');
// For storing in redis // For storing in redis
// options[type] = redis // options[type] = redis
@@ -9,9 +8,13 @@ var hashlib = require('hashlib');
// options[db] - The db to use (default 0) // options[db] - The db to use (default 0)
// options[expire] - The time to live for each key set (default never) // options[expire] - The time to live for each key set (default never)
var RedisDocumentStore = function(options) { var RedisDocumentStore = function(options, client) {
this.expire = options.expire; this.expire = options.expire;
if (!RedisDocumentStore.client) { if (client) {
winston.info('using predefined redis client');
RedisDocumentStore.client = client;
} else if (!RedisDocumentStore.client) {
winston.info('configuring redis');
RedisDocumentStore.connect(options); RedisDocumentStore.connect(options);
} }
}; };
@@ -22,9 +25,16 @@ 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('error connecting to redis index ' + index, { error: err.message }); winston.error(
'error connecting to redis index ' + index,
{ error: err }
);
process.exit(1); process.exit(1);
} }
else { else {

View File

@@ -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

View File

@@ -1,12 +1,17 @@
var RandomKeyGenerator = function(options) { var RandomKeyGenerator = function(options) {
if (!options) {
options = {};
}
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
}; };
// Generate a random key // Generate a random key
RandomKeyGenerator.prototype.createKey = function(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++) {
text += this.keyspace.charAt(Math.floor(Math.random() * this.keyspace.length)); index = Math.floor(Math.random() * this.keyspace.length);
text += this.keyspace.charAt(index);
} }
return text; return text;
}; };

View File

@@ -1,52 +1,49 @@
{ {
"name": "haste",
"name": "haste", "version": "0.1.0",
"version": "0.0.1", "private": true,
"description": "Private Pastebin Server",
"private": true, "keywords": [
"paste",
"description": "Private Paste", "pastebin"
],
"keywords": [ "paste", "pastebin" ], "author": {
"name": "John Crepezzi",
"author": { "email": "john.crepezzi@gmail.com",
"name": "John Crepezzi", "url": "http://seejohncode.com/"
"email": "john.crepezzi@gmail.com",
"url": "http://seejohncode.com/"
},
"main": "haste",
"dependencies": {
"winston": "*",
"hashlib": "*",
"connect": "*",
"uglify-js": "*"
},
"devDependencies": {
"jasmine-node": "*"
}, },
"main": "haste",
"bundledDependencies": [], "dependencies": {
"connect-ratelimit": "0.0.7",
"engines": { "connect-route": "0.1.5",
"node": "*" "connect": "3.4.1",
}, "st": "1.1.0",
"winston": "0.6.2",
"bin": { "uglify-js": "1.3.3",
"haste-server": "./server.js" "busboy": "0.2.4",
}, "pg": "4.1.1"
},
"files": [ "server.js", "lib", "static" ], "devDependencies": {
"mocha": "*",
"directories": { "should": "*"
"lib": "./lib" },
}, "bundledDependencies": [],
"engines": {
"scripts": { "node": "0.10.35"
"start": "node server.js", },
"test": "jasmine-node spec" "bin": {
} "haste-server": "./server.js"
},
"files": [
"server.js",
"lib",
"static"
],
"directories": {
"lib": "./lib"
},
"scripts": {
"start": "node server.js",
"test": "mocha -r should spec/*"
}
} }

138
server.js
View File

@@ -4,13 +4,16 @@ 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');
// Load the configuration and set some defaults // Load the configuration and set some defaults
var config = JSON.parse(fs.readFileSync('config.js', 'utf8')); var config = JSON.parse(fs.readFileSync('./config.js', 'utf8'));
config.port = config.port || 7777; config.port = process.env.PORT || config.port || 7777;
config.host = config.host || 'localhost'; config.host = process.env.HOST || config.host || 'localhost';
// Set up the logger // Set up the logger
if (config.logging) { if (config.logging) {
@@ -34,8 +37,18 @@ if (!config.storage) {
if (!config.storage.type) { if (!config.storage.type) {
config.storage.type = 'file'; config.storage.type = 'file';
} }
var Store = require('./lib/' + config.storage.type + '_document_store');
var preferredStore = new Store(config.storage); var Store, preferredStore;
if (process.env.REDISTOGO_URL && config.storage.type === 'redis') {
var redisClient = require('redis-url').connect(process.env.REDISTOGO_URL);
Store = require('./lib/document_stores/redis');
preferredStore = new Store(config.storage, redisClient);
}
else {
Store = require('./lib/document_stores/' + config.storage.type);
preferredStore = new Store(config.storage);
}
// Compress the static javascript assets // Compress the static javascript assets
if (config.recompressStaticAssets) { if (config.recompressStaticAssets) {
@@ -45,8 +58,10 @@ if (config.recompressStaticAssets) {
for (var i = 0; i < list.length; i++) { for (var i = 0; i < list.length; i++) {
var item = list[i]; var item = list[i];
var orig_code, ast; var orig_code, ast;
if ((item.indexOf('.js') === item.length - 3) && (item.indexOf('.min.js') === -1)) { if ((item.indexOf('.js') === item.length - 3) &&
dest = item.substring(0, item.length - 3) + '.min' + item.substring(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'); orig_code = fs.readFileSync('./static/' + item, 'utf8');
ast = jsp.parse(orig_code); ast = jsp.parse(orig_code);
ast = pro.ast_mangle(ast); ast = pro.ast_mangle(ast);
@@ -58,18 +73,19 @@ if (config.recompressStaticAssets) {
} }
// Send the static documents into the preferred store, skipping expirations // Send the static documents into the preferred store, skipping expirations
var path, data;
for (var name in config.documents) { for (var name in config.documents) {
var path = config.documents[name]; path = config.documents[name];
fs.readFile(path, 'utf8', function(err, data) { data = fs.readFileSync(path, 'utf8');
if (data && !err) { winston.info('loading static document', { name: name, path: path });
preferredStore.set(name, data, function(cb) { if (data) {
winston.info('loaded static document', { name: name, path: path }); preferredStore.set(name, data, function(cb) {
}, true); winston.debug('loaded static document', { success: cb });
} }, true);
else { }
winston.warn('failed to load static document', { name: name, path: path }); else {
} winston.warn('failed to load static document', { name: name, path: path });
}); }
} }
// Pick up a key generator // Pick up a key generator
@@ -86,38 +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) {
// get raw documents - support getting with extension config.rateLimits.end = true;
app.get('/raw/:id', function(request, response, next) { app.use(connect_rate_limit(config.rateLimits));
var skipExpire = !!config.documents[request.params.id]; }
var key = request.params.id.split('.')[0];
return documentHandler.handleRawGet(key, response, skipExpire); // first look at API calls
}); app.use(route(function(router) {
// add documents // get raw documents - support getting with extension
app.post('/documents', function(request, response, next) { router.get('/raw/:id', function(request, response, next) {
return documentHandler.handlePost(request, response); var key = request.params.id.split('.')[0];
}); var skipExpire = !!config.documents[key];
// get documents return documentHandler.handleRawGet(key, response, skipExpire);
app.get('/documents/:id', function(request, response, next) { });
var skipExpire = !!config.documents[request.params.id]; // add documents
return documentHandler.handleGet(request.params.id, response, skipExpire); router.post('/documents', function(request, response, next) {
}); return documentHandler.handlePost(request, response);
}), });
// Otherwise, static // get documents
connect.staticCache(), router.get('/documents/:id', function(request, response, next) {
connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }), var key = request.params.id.split('.')[0];
// Then we can loop back - and everything else should be a token, var skipExpire = !!config.documents[key];
// so route it back to /index.html return documentHandler.handleGet(key, response, skipExpire);
connect.router(function(app) { });
app.get('/:id', function(request, response, next) { }));
request.url = request.originalUrl = '/index.html';
next(); // Otherwise, try to match static files
}); app.use(connect_st({
}), path: __dirname + '/static',
connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }) content: { maxAge: config.staticMaxAge },
).listen(config.port, config.host); passthrough: true,
index: false
}));
// 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();
});
}));
// 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);

View File

@@ -1,17 +1,20 @@
var DocumentHandler = require('../lib/document_handler'); var DocumentHandler = require('../lib/document_handler');
var Generator = require('../lib/key_generators/random');
describe('document_handler', function() { describe('document_handler', function() {
describe('randomKey', function() { describe('randomKey', function() {
it('should choose a key of the proper length', function() { it('should choose a key of the proper length', function() {
var dh = new DocumentHandler({ keyLength: 6 }); var gen = new Generator();
expect(dh.randomKey().length).toBe(6); var dh = new DocumentHandler({ keyLength: 6, keyGenerator: gen });
dh.acceptableKey().length.should.equal(6);
}); });
it('should choose a default key length', function() { it('should choose a default key length', function() {
var dh = new DocumentHandler(); var gen = new Generator();
expect(dh.keyLength).toBe(DocumentHandler.defaultKeyLength); var dh = new DocumentHandler({ keyGenerator: gen });
dh.keyLength.should.equal(DocumentHandler.defaultKeyLength);
}); });
}); });

View File

@@ -1,4 +1,4 @@
var RedisDocumentStore = require('../lib/redis_document_store'); var RedisDocumentStore = require('../lib/document_stores/redis');
var winston = require('winston'); var winston = require('winston');
winston.remove(winston.transports.Console); winston.remove(winston.transports.Console);
@@ -15,73 +15,34 @@ describe('redis_document_store', function() {
describe('set', function() { describe('set', function() {
it('should be able to set a key and have an expiration set', function() { it('should be able to set a key and have an expiration set', function(done) {
var store = new RedisDocumentStore({ expire: 10 }); var store = new RedisDocumentStore({ expire: 10 });
runs(function() { store.set('hello1', 'world', function() {
var _this = this;
store.set('hello1', 'world', function(worked) {
_this.result = worked;
});
});
waitsFor(function() {
return typeof(this.result) === 'boolean';
});
runs(function() {
var _this = this;
RedisDocumentStore.client.ttl('hello1', function(err, res) { RedisDocumentStore.client.ttl('hello1', function(err, res) {
expect(res).toBeGreaterThan(1); res.should.be.above(1);
_this.done = true; done();
}); });
}); });
waitsFor(function() {
return this.done;
});
}); });
it('should not set an expiration when told not to', function() { it('should not set an expiration when told not to', function(done) {
var store = new RedisDocumentStore({ expire: 10 }); var store = new RedisDocumentStore({ expire: 10 });
runs(function() { store.set('hello2', 'world', function() {
var _this = this;
store.set('hello2', 'world', function(worked) {
_this.result = worked;
}, true);
});
waitsFor(function() {
return typeof(this.result) === 'boolean';
});
runs(function() {
var _this = this;
RedisDocumentStore.client.ttl('hello2', function(err, res) { RedisDocumentStore.client.ttl('hello2', function(err, res) {
expect(res).toBe(-1); res.should.equal(-1);
_this.done = true; done();
}); });
}); }, true);
waitsFor(function() {
return this.done;
});
}); });
it('should not set an expiration when expiration is off', function() { it('should not set an expiration when expiration is off', function(done) {
var store = new RedisDocumentStore({ expire: false }); var store = new RedisDocumentStore({ expire: false });
runs(function() { store.set('hello3', 'world', function(worked) {
var _this = this;
store.set('hello3', 'world', function(worked) {
_this.result = worked;
});
});
waitsFor(function() {
return typeof(this.result) === 'boolean';
});
runs(function() {
var _this = this;
RedisDocumentStore.client.ttl('hello3', function(err, res) { RedisDocumentStore.client.ttl('hello3', function(err, res) {
expect(res).toBe(-1); res.should.equal(-1);
_this.done = true; done();
}); });
}); });
waitsFor(function() {
return this.done;
});
}); });
}); });

View File

@@ -42,6 +42,8 @@ textarea {
border: 0px; border: 0px;
outline: none; outline: none;
font-size: 13px; font-size: 13px;
padding-right: 360px;
overflow: inherit;
} }
#box code { #box code {
@@ -147,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;
@@ -166,3 +169,4 @@ textarea {
#messages li.error { #messages li.error {
background:rgba(102,8,0,0.8); background:rgba(102,8,0,0.8);
} }

View File

@@ -62,6 +62,7 @@ haste_document.prototype.save = function(data, callback) {
type: 'post', type: 'post',
data: data, data: data,
dataType: 'json', dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function(res) { success: function(res) {
_this.locked = true; _this.locked = true;
_this.key = res.key; _this.key = res.key;
@@ -165,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: '' 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
@@ -300,16 +302,16 @@ haste.prototype.configureButtons = function() {
}, },
shortcutDescription: 'control + shift + r', shortcutDescription: 'control + shift + r',
action: function() { action: function() {
window.open('/raw/' + _this.doc.key); window.location.href = '/raw/' + _this.doc.key;
} }
}, },
{ {
$where: $('#box2 .twitter'), $where: $('#box2 .twitter'),
label: 'Twitter', label: 'Twitter',
shortcut: function(evt) { shortcut: function(evt) {
return _this.options.twitter && _this.doc.locked && evt.ctrlKey && evt.keyCode == 84; return _this.options.twitter && _this.doc.locked && evt.shiftKey && evt.ctrlKey && evt.keyCode == 84;
}, },
shortcutDescription: 'control + t', shortcutDescription: 'control + shift + t',
action: function() { action: function() {
window.open('https://twitter.com/share?url=' + encodeURI(window.location.href)); window.open('https://twitter.com/share?url=' + encodeURI(window.location.href));
} }

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

View File

@@ -3,11 +3,11 @@
<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"/>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="highlight.min.js"></script> <script type="text/javascript" src="highlight.min.js"></script>
<script type="text/javascript" src="application.min.js"></script> <script type="text/javascript" src="application.min.js"></script>
@@ -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>

View File

@@ -4,95 +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;
} }
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;
}