We make no claim to hold any copyright to the images posted on Dropular. All of the content on this site (including graphics, sounds, videos and other files) copyright their respective owners.
42 |
Our goal is to promote great content and link back to the original source. If you are the owner of an item on Dropular and do not wish to have it promoted on Dropular.net, we will simply remove the item if you provide the proper details and information asked below.
43 |
44 |
To file a copyright infringement notification with us, please follow the steps below:
45 |
46 |
47 |
1. Copy the item URL: and paste it into an e-mail.
48 |
2. Add information about the original copyrighted work, including the full name of the person / company / party holding the copyright.
49 |
3. Your full name used as guarantor that the above is accurate and not deceptive or false.
50 |
4. Send the e-mail to dropular (at) gmail (dot) com
51 |
52 |
53 |
That's it!
54 |
55 |
56 |
--------------------------------------------------------------------------------
/client/jquery.continuum.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Continuum provides a way of filling in content when reaching the end of
3 | * scroll in a document.
4 | *
5 | * Released under an MIT license:
6 | *
7 | * Copyright (c) 2010 Rasmus Andersson
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | */
27 | (function($){
28 |
29 | $.fn.continuum = function(options, callback) {
30 | var ctx = $.extend({
31 | threshold: 400
32 | }, typeof options === 'object' || {filler: options});
33 |
34 | // Keep single set of refrences and jQuery objects. No leakin' here bouy!
35 | if (!$.continuum) {
36 | $.continuum = {
37 | '$doc': $(document),
38 | '$win': $(window)
39 | };
40 | }
41 |
42 | // Locals hanging out at the pub
43 | var $doc = $.continuum.$doc,
44 | $win = $.continuum.$win,
45 | self = this,
46 | winHeight = $win.height();
47 |
48 | /* var onscroll = function(){
49 | var distance = $doc.height() - window.pageYOffset - winHeight;
50 | // on MSIE, replace window.pageYOffset with document.body.scrollTop
51 | if (distance < ctx.threshold) {
52 | //console.log('distance < threshold -- ', distance, '<', ctx.threshold);
53 | ctx.waiting = true;
54 | $win.unbind('scroll.continuum');
55 | ctx.filler.call(this, function(stop){
56 | ctx.waiting = false;
57 | if (stop) {
58 | ctx.stopped = true;
59 | } else {
60 | $win.bind('scroll.continuum', onscroll);
61 | }
62 | });
63 | }
64 | };
65 | */
66 |
67 | //console.log('distance < threshold -- ', distance, '<', ctx.threshold);
68 | ctx.waiting = true;
69 | ctx.filler.call(this, function(stop){
70 | ctx.waiting = false;
71 | if (stop) {
72 | ctx.stopped = true;
73 | }
74 | });
75 |
76 | // Adjust window height when needed
77 | // $win.bind('resize.continuum', function(){ winHeight = $win.height(); });
78 |
79 | // Check distance when scrolling
80 | //$win.bind('scroll.continuum', onscroll);
81 |
82 | return this;
83 | };
84 |
85 | })(jQuery);
--------------------------------------------------------------------------------
/client/about/privacy.html:
--------------------------------------------------------------------------------
1 |
4 |
42 |
43 |
44 |
Privacy policy
45 |
46 |
47 | Respecting user privacy is important to Dropular.net. Read this to learn about personal information that Dropular.net collects and how it may be used.
48 |
49 |
50 |
Dropular.net database
51 |
52 | The e-mail address you register with Dropular.net and the logs of your activity is used to calculate personalized recommendations as part of our service. We use this information in aggregate to generate statistics and other usage information. We share this information anonymously with you, other end users, and other third parties.
53 |
54 |
55 |
Links to third party web sites
56 |
57 | Our service may contain links to other websites and software. We are not responsible for the privacy practices or the content of these websites or software. Please visit the privacy policies of these third party sites in order to understand their privacy and information collection practices.
58 |
59 |
60 |
61 |
Disclosures required by law
62 |
63 | We reserve the right to disclose your personally identifiable information when we believe in good faith that an applicable law, regulation, or legal process requires it, or when we believe disclosure is necessary to protect or enforce our rights or the rights of another user.
64 |
65 |
66 |
Cookies
67 |
68 | Cookies are small text files stored by your browser on your computer when you visit a website. We use cookies to improve our website and make it easier to use. Cookies permit us to recognize you and avoid repetitive requests for the same information. Most browsers will accept cookies until you change your browser settings to refuse them. You may change your browser's settings to refuse our cookies.
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/lib/aws/pool.js:
--------------------------------------------------------------------------------
1 | var sys = require('sys');
2 |
3 | // ----------------------------------------------------------------------------
4 | // Simple instance pool (aka free list)
5 |
6 | function Pool(keep, limit) {
7 | process.EventEmitter.call(this);
8 | this.keep = keep || 0;
9 | this.limit = limit || 128;
10 | this.free = []; // push used to end/right, shift new from front/left
11 | this.busy = 0;
12 | this.getQueue = [];
13 | }
14 | exports.Pool = Pool;
15 | sys.inherits(Pool, process.EventEmitter);
16 | Pool.prototype.create = function() { throw new Error('not implemeted'); }
17 | Pool.prototype.get = function(callback) {
18 | var instance = this.free.shift();
19 | if (!instance) {
20 | if (this.busy < this.limit) {
21 | instance = this.create();
22 | } else {
23 | if (callback) this.getQueue.push(callback);
24 | return;
25 | }
26 | }
27 | this.busy++;
28 | if (callback) callback(null, instance);
29 | return instance;
30 | }
31 | Pool.prototype.cancelGet = function(callbackToCancel) {
32 | var i = this.getQueue.indexOf(callbackToCancel), found = (i !== -1);
33 | if (found) this.getQueue.splice(i,1);
34 | return found;
35 | }
36 | Pool.prototype.put = function(instance) {
37 | if (this.getQueue.length) {
38 | this.getQueue.shift()(null, instance);
39 | } else {
40 | this.busy--;
41 | if (this.free.length < this.keep) this.free.push(instance);
42 | else this.destroy(instance);
43 | }
44 | }
45 | Pool.prototype.remove = function(item, noDestroy) {
46 | var i = this.free.indexOf(item), found = (i !== -1);
47 | if (found) this.free.splice(i,1);
48 | if (!noDestroy) this.destroy(item);
49 | return found;
50 | }
51 | Pool.prototype.removeAll = function(noDestroy) {
52 | if (!noDestroy)
53 | for (var i=0,item; (item = this.free[i]); i++) this.destroy(item);
54 | this.free = [];
55 | }
56 | Pool.prototype.destroy = function(item) { }
57 |
58 | // ----------------------------------------------------------------------------
59 | // HTTP connection pool
60 |
61 | var http = require('http');
62 |
63 | function HTTPConnectionPool(keep, limit, port, host, secure) {
64 | Pool.call(this, keep, limit);
65 | this.port = port;
66 | this.host = host;
67 | this.secure = secure;
68 | var self = this;
69 | process.addListener("exit", function (){
70 | // avoid lingering FDs
71 | try { self.removeAll(); }catch(e){}
72 | try { delete self; }catch(e){}
73 | });
74 | }
75 | exports.HTTPConnectionPool = HTTPConnectionPool;
76 | sys.inherits(HTTPConnectionPool, Pool);
77 | HTTPConnectionPool.prototype.create = function(){
78 | var self = this, conn = http.createClient(this.port, this.host);
79 | if (this.secure) {
80 | if (typeof this.secure !== 'object') this.secure = {};
81 | conn.setSecure('X509_PEM', this.secure.ca_certs, this.secure.crl_list,
82 | this.secure.private_key, this.secure.certificate);
83 | }
84 | conn._onclose = function(hadError, reason) {
85 | self.remove(conn);
86 | if (hadError)
87 | self.emit('error', new Error('Connection error'+(reason ? ': '+reason : '')));
88 | try { conn.removeListener('close', conn._onclose); }catch(e){}
89 | }
90 | conn.addListener('close', conn._onclose);
91 | return conn;
92 | }
93 | HTTPConnectionPool.prototype.destroy = function(conn){
94 | try { conn.removeListener('close', conn._onclose); }catch(e){}
95 | try { conn.end(); }catch(e){}
96 | }
--------------------------------------------------------------------------------
/misc/data/import-batch.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node --
2 | // This program imports a batch of documents into couchdb.
3 | //
4 | // usage: import-batch.js ..
5 | // - Each line of should be a document i.e. {"_id":"foo","field1":123}.
6 | // - A line not starting with a "{" is ignored.
7 | // - Any trailing "," and whitespace on a line is ignored and can thus exists.
8 | //
9 | var sys = require('sys'),
10 | fs = require('fs'),
11 | querystring = require('querystring'),
12 | http = require('http'),
13 | couchdb = require('../../lib/couchdb');
14 |
15 | // Parse options and load data
16 | var lines = '';
17 | var database = process.argv[2];
18 | if (!database) {
19 | sys.error('usage: import-batch.js ..');
20 | process.exit(1);
21 | } else if (database.charAt(0) === '{') {
22 | database = JSON.parse(database);
23 | } else if ((p = database.indexOf(':')) !== -1) {
24 | var db = '';
25 | if ((p2 = database.indexOf('/')) !== -1) {
26 | db = database.substr(p2+1);
27 | database = database.substr(0, p2);
28 | }
29 | database = {
30 | host: database.substr(0, p),
31 | port: parseInt(database.substr(p+1)),
32 | db: db
33 | }
34 | }
35 | var db = new couchdb.Db(database);
36 | // check for db existance
37 | db.get('', function(err, r) {
38 | if (!err) return;
39 | if (err.couchDbError === 'not_found') sys.error('no such database '+sys.inspect(database));
40 | else sys.error('['+err.couchDbError+'] '+err);
41 | process.exit(1);
42 | });
43 | // go on and load data
44 | process.argv.slice(3).forEach(function(filename){
45 | try {
46 | lines += fs.readFileSync(filename);
47 | } catch (e) {
48 | if (e.message === 'No such file or directory') {
49 | sys.error(e.message+': '+filename);
50 | process.exit(1);
51 | }
52 | throw e;
53 | }
54 | });
55 | if (lines.length === 0) {
56 | sys.error('No input files or they are empty -- aborting');
57 | process.exit(1);
58 | }
59 | lines = lines.trim().split('\n');
60 | sys.log('loaded '+lines.length+' prepared documents');
61 |
62 | // proceed with sequentially submitted batches of 100
63 | function postNextBatch(offset, length) {
64 | var body = lines.slice(offset, offset+length);
65 | var count = body.length;
66 | body = '{"docs":[' + body.filter(function(line){
67 | return line.length && line.charAt(0) === '{';
68 | }).map(function(line){
69 | return line.replace(/[,\n ]+$/,'');
70 | }).join(',\n') + ']}';
71 | db.post('_bulk_docs', body, function(err, result) {
72 | if (err) sys.error(err.stack);
73 | else {
74 | // ignore conflict errors, but abort on other errors
75 | if (Array.isArray(result)) {
76 | var hadError = false;
77 | result.forEach(function(status){
78 | if (status.error && status.error !== 'conflict') {
79 | sys.error('couchdb ['+status.error+'] '+status.reason+' (key: '+status.id+')');
80 | hadError = true;
81 | }
82 | });
83 | if (hadError) return;
84 | } else if (typeof result === 'object' && result.error) {
85 | sys.error('couchdb ['+status.error+'] '+status.reason+' -- '+sys.inspect(result));
86 | return;
87 | }
88 | sys.log('posted ['+offset+','+count+'] ('+(offset+length)+' of '+lines.length+')');
89 | if (count === length)
90 | postNextBatch(offset+length, length);
91 | }
92 | });
93 | }
94 | postNextBatch(0, 100);
95 |
--------------------------------------------------------------------------------
/client/about/registrations-will-soon-open.html:
--------------------------------------------------------------------------------
1 |
42 |
84 |
85 |
Registrations will soon open
86 |
Let us know your email and we'll let you know when registrations are open.
87 |
88 | Thanks! We'll keep in touch.
89 |
90 |
91 | You're already listed — speak to you soon.
92 |
Dropular is a micro-blogging service which makes it possible for members to save and collect their favorite images found from various sources online. Dropular is currently an invite only service and mostly designed for fashion designers, photographers, architects, graphic designers and artists.
48 |
49 |
Copyright infringement notification
50 |
We make no claim to hold any copyright to the images posted on Dropular. All of the content on this site (including graphics, sounds, videos and other files) copyright their respective owners.
51 |
Original source of each item can be found next to each and every "drop".
52 |
53 |
Our goal is to promote great content and link back to the original source. If you are the owner of an item on Dropular and do not wish to have it promoted on Dropular.net, we will simply remove the item if you provide the proper details and information asked below.
54 |
55 |
To file a copyright infringement notification with us, please send an email containing the following requirements:
56 |
57 |
58 |
1. The complete URL on Dropular to the item http://dropular.net/#drops/DROP-ID
59 |
2. Information about the original copyrighted work, including the full name of the person / company / party holding the copyright.
60 |
3. Your full name used as guarantor that the above is accurate and not deceptive or false.
23 |
24 |
25 |
26 | Welcome to Dropular version (3).002.
27 | Things are still being developed and
28 | we guarantee that you will stumble
29 | upon bugs and errors during your visit.
30 |
31 | We constantly working on new
32 | features that can extend our service
33 | and make Dropular much more convenient
34 | and ease to use.
35 |
36 | For updates,
37 | read our blog
38 | and add us on Twitter.
39 |
40 |
41 |
42 |
43 |
44 |
45 | Dropular is a micro-blogging service which makes it possible for members to save and collect their favorite images found from various sources online. Dropular is currently an invite only service and mostly designed for fashion designers, photographers, architects, graphic designers and artists.
46 |
47 |
83 |
84 |
85 |
86 |
87 |
92 |
93 |
94 |
95 |
99 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/lib/base62.js:
--------------------------------------------------------------------------------
1 | var Base62 = function(pattern, encoder, ignore) {
2 | this.parser = new Parser(ignore);
3 | if (pattern) this.parser.put(pattern, "");
4 | this.encoder = encoder;
5 | };
6 |
7 | Base62.WORDS = /\b[\da-zA-Z]\b|\w{2,}/g;
8 |
9 | Base62.ENCODE10 = "String";
10 | Base62.ENCODE36 = "function(c){return c.toString(36)}";
11 | Base62.ENCODE62 = "function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))}";
12 |
13 | Base62.UNPACK = "eval(function(p,a,c,k,e,r){e=%5;if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];" +
14 | "k=[function(e){return r[e]||e}];e=function(){return'%6'};c=1};while(c--)if(k[c])p=p." +
15 | "replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('%1',%2,%3,'%4'.split('|'),0,{}))";
16 |
17 | mixin(Base62.prototype, {
18 | parser: null,
19 | encoder: undefined,
20 |
21 | search: function(script) {
22 | var words = new Words;
23 | this.parser.putAt(-1, function(word) {
24 | words.add(word);
25 | });
26 | this.parser.exec(script);
27 | return words;
28 | },
29 |
30 | encode: function(script) {
31 | var words = this.search(script);
32 |
33 | words.sort();
34 |
35 | var encoded = new Collection; // a dictionary of base62 -> base10
36 | var size = words.size();
37 | for (var i = 0; i < size; i++) {
38 | encoded.put(Packer.encode62(i), i);
39 | }
40 |
41 | function replacement(word) {
42 | return words["#" + word].replacement;
43 | };
44 |
45 | var empty = K("");
46 | var index = 0;
47 | forEach (words, function(word) {
48 | if (encoded.has(word)) {
49 | word.index = encoded.get(word);
50 | word.toString = empty;
51 | } else {
52 | while (words.has(Packer.encode62(index))) index++;
53 | word.index = index++;
54 | if (word.count == 1) {
55 | word.toString = empty;
56 | }
57 | }
58 | word.replacement = Packer.encode62(word.index);
59 | if (word.replacement.length == word.toString().length) {
60 | word.toString = empty;
61 | }
62 | });
63 |
64 | // sort by encoding
65 | words.sort(function(word1, word2) {
66 | return word1.index - word2.index;
67 | });
68 |
69 | // trim unencoded words
70 | words = words.slice(0, this.getKeyWords(words).split("|").length);
71 |
72 | script = script.replace(this.getPattern(words), replacement);
73 |
74 | /* build the packed script */
75 |
76 | var p = this.escape(script);
77 | var a = "[]";
78 | var c = this.getCount(words);
79 | var k = this.getKeyWords(words);
80 | var e = this.getEncoder(words);
81 | var d = this.getDecoder(words);
82 |
83 | // the whole thing
84 | return format(Base62.UNPACK, p,a,c,k,e,d);
85 | },
86 |
87 | search: function(script) {
88 | var words = new Words;
89 | forEach (script.match(Base62.WORDS), words.add, words);
90 | return words;
91 | },
92 |
93 | escape: function(script) {
94 | // Single quotes wrap the final string so escape them.
95 | // Also, escape new lines (required by conditional comments).
96 | return script.replace(/([\\'])/g, "\\$1").replace(/[\r\n]+/g, "\\n");
97 | },
98 |
99 | getCount: function(words) {
100 | return words.size() || 1;
101 | },
102 |
103 | getDecoder: function(words) {
104 | // returns a pattern used for fast decoding of the packed script
105 | var trim = new RegGrp({
106 | "(\\d)(\\|\\d)+\\|(\\d)": "$1-$3",
107 | "([a-z])(\\|[a-z])+\\|([a-z])": "$1-$3",
108 | "([A-Z])(\\|[A-Z])+\\|([A-Z])": "$1-$3",
109 | "\\|": ""
110 | });
111 | var pattern = trim.exec(words.map(function(word) {
112 | if (word.toString()) return word.replacement;
113 | return "";
114 | }).slice(0, 62).join("|"));
115 |
116 | if (!pattern) return "^$";
117 |
118 | pattern = "[" + pattern + "]";
119 |
120 | var size = words.size();
121 | if (size > 62) {
122 | pattern = "(" + pattern + "|";
123 | var c = Packer.encode62(size).charAt(0);
124 | if (c > "9") {
125 | pattern += "[\\\\d";
126 | if (c >= "a") {
127 | pattern += "a";
128 | if (c >= "z") {
129 | pattern += "-z";
130 | if (c >= "A") {
131 | pattern += "A";
132 | if (c > "A") pattern += "-" + c;
133 | }
134 | } else if (c == "b") {
135 | pattern += "-" + c;
136 | }
137 | }
138 | pattern += "]";
139 | } else if (c == 9) {
140 | pattern += "\\\\d";
141 | } else if (c == 2) {
142 | pattern += "[12]";
143 | } else if (c == 1) {
144 | pattern += "1";
145 | } else {
146 | pattern += "[1-" + c + "]";
147 | }
148 |
149 | pattern += "\\\\w)";
150 | }
151 | return pattern;
152 | },
153 |
154 | getEncoder: function(words) {
155 | var size = words.size();
156 | return Base62["ENCODE" + (size > 10 ? size > 36 ? 62 : 36 : 10)];
157 | },
158 |
159 | getKeyWords: function(words) {
160 | return words.map(String).join("|").replace(/\|+$/, "");
161 | },
162 |
163 | getPattern: function(words) {
164 | var words = words.map(String).join("|").replace(/\|{2,}/g, "|").replace(/^\|+|\|+$/g, "") || "\\x0";
165 | return new RegExp("\\b(" + words + ")\\b", "g");
166 | }
167 | });
168 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dropular.net version 2010
2 |
3 | Note: This is an archived snapshot of the dropular.net source, May 2010. Some parts has been removed or altered for the sake of security and readability.
4 |
5 | ## Development
6 |
7 | ### Starting daily development
8 |
9 | Terminal 1:
10 |
11 | ssh -L5984:127.0.0.1:5984 dropular-ec2-1
12 |
13 | Terminal 2:
14 |
15 | cd dropular/dropular
16 | ./build.sh
17 |
18 | Terminal 3:
19 |
20 | cd dropular/dropular
21 | ./httpd.js
22 |
23 | ### Setting up a local installation
24 |
25 | **1. Install node**
26 |
27 | You'll need [Node](http://nodejs.org/) v0.1.32 or later.
28 |
29 | $ git clone git://github.com/ry/node.git
30 | $ cd node
31 | $ ./configure
32 | $ make
33 | $ sudo make install
34 |
35 | **2. Install oui and the dropular site.**
36 |
37 | $ mkdir dropular && cd dropular
38 | $ git clone git://github.com/rsms/oui.git
39 | $ git clone git://github.com/rsms/node-couchdb-min.git
40 | $ git clone git@github.com:rsms/dropular.git
41 |
42 | You might need to build the oui library:
43 |
44 | $ cd dropular/oui/client
45 | $ ./build.sh -s
46 |
47 | **3. Install CouchDB**
48 |
49 | First, install dependencies.
50 | OS X:
51 |
52 | $ sudo port install icu erlang spidermonkey curl
53 |
54 | Debian:
55 |
56 | $ sudo apt-get install build-essential erlang libicu-dev libmozjs-dev\
57 | libcurl4-openssl-dev
58 |
59 | Suck down and build CouchDB:
60 |
61 | $ svn co http://svn.apache.org/repos/asf/couchdb/trunk couchdb
62 | $ cd couchdb
63 | $ ./bootstrap && ./configure
64 | $ make && sudo make install
65 |
66 |
67 | ----
68 |
69 | [--deprecated--]
70 |
71 | In production, you should configure for prefix "" (that's "/"):
72 |
73 | $ ./configure --prefix ''
74 |
75 | And perform the following after `make install`:
76 |
77 | $ sudo adduser --system --home /var/lib/couchdb --no-create-home\
78 | --shell /bin/bash --group --gecos 'CouchDB Administrator' couchdb
79 | $ sudo chown -R couchdb.couchdb /var/{lib,log,run}/couchdb /etc/couchdb
80 | $ sudo chmod -R 0770 /var/{lib,log,run}/couchdb /etc/couchdb
81 |
82 | Uninstall is partially possible by running `make uninstall` and then `sudo find /usr/local -iname '*couch*' | sudo xargs rm -rf`.
83 |
84 | Create a dropular user and group:
85 |
86 | $ sudo adduser --system --home /var/dropular --shell /bin/bash --group\
87 | --gecos 'Dropular system user' dropular
88 |
89 | Generate an SSH key for the dropular user:
90 |
91 | $ sudo su dropular
92 | $ cd
93 | $ ssh-keygen -t rsa
94 | $ cat .ssh/id_rsa.pub
95 |
96 | Copy the output from the last command and add a new "deploy key" in https://github.com/rsms/dropular/edit
97 |
98 | Check out the dropular repo, logged in as `dropular`:
99 |
100 | $ git clone git@github.com:rsms/dropular.git /var/dropular/dropular
101 |
102 | Create a symlink in `/var/www`:
103 |
104 | $ ln -s /var/dropular/dropular /var/www/dropular.net/www
105 |
106 | #### Deploying a new version of dropular-httpd
107 |
108 | First thing, start a server instance in debug mode on an unused port:
109 |
110 | $ sudo -u dropular /var/dropular/dropular/httpd.js -d -p 9000
111 |
112 | Then, test with a client directly:
113 |
114 | $ open 'http://dropular.net:9000/'
115 |
116 | Then test with a client partially:
117 |
118 | $ open 'http://dropular.net/#OUI_DEBUG_BACKEND=dropular.net:9000'
119 |
120 | If everything look jolly good, restart all live server instances:
121 |
122 | $ sudo invoke-rc.d dropular-httpd restart
123 |
124 | Aaand test the live client:
125 |
126 | $ open 'http://dropular.net/'
127 |
128 |
129 | ### Client development
130 |
131 | When developing the client, you need to build it. The `build.sh` file does this for you automatically. In a terminal:
132 |
133 | $ ./build.sh -fO 0
134 |
135 | > `-fO 0` -- The `f` flag causes the first build to be "complete" (forced). The `O` flag sets the optimization level to zero, making debugging possible.
136 |
137 | Keep this running in a terminal -- when a file has been changed, the client will automatically be rebuilt.
138 |
139 |
140 | ### Server development
141 |
142 | When developing the server, you need to (re)start `httpd.js` when something changes in the server code.
143 |
144 | $ ./httpd.js -d
145 |
146 | > `-d` -- The `d` flag causes debugging to be enabled.
147 |
148 |
149 | ## MIT license
150 |
151 | Copyright (c) 2009-2010 Rasmus Andersson , Andreas Pihlström
152 |
153 | Permission is hereby granted, free of charge, to any person obtaining a copy
154 | of this software and associated documentation files (the "Software"), to deal
155 | in the Software without restriction, including without limitation the rights
156 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
157 | copies of the Software, and to permit persons to whom the Software is
158 | furnished to do so, subject to the following conditions:
159 |
160 | The above copyright notice and this permission notice shall be included in
161 | all copies or substantial portions of the Software.
162 |
163 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
164 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
165 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
166 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
167 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
168 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
169 | THE SOFTWARE.
170 |
--------------------------------------------------------------------------------
/lib/aws/s3.js:
--------------------------------------------------------------------------------
1 | var http = require("http"),
2 | sys = require("sys"),
3 | sha1 = require('./sha1'),
4 | httputil = require('./httputil');
5 |
6 | function Bucket(id, key, secret){
7 | this.id = id;
8 | this.key = key;
9 | this.secret = secret;
10 | }
11 | exports.Bucket = Bucket;
12 |
13 | Bucket.prototype.urlTo = function(path){
14 | return 'http://'+
15 | (this.id ? this.id+'.' : '') + 's3.amazonaws.com'+
16 | path;
17 | }
18 |
19 | Bucket.prototype.request = function(options, callback){
20 | if (typeof options === 'function') { callback = options; options = undefined; }
21 |
22 | // Default options
23 | var opt = {
24 | host: (this.id ? this.id+'.' : '') + 's3.amazonaws.com',
25 | //debug: true,
26 | headers: {},
27 | ctxid: module.id
28 | };
29 |
30 | // mixin options
31 | if (typeof options === 'object') {
32 | // method, path, query, body, contentType, headers
33 | for (var k in options) opt[k] = options[k];
34 | }
35 |
36 | // create request
37 | var ev = httputil.request(opt, function(err, data, res){
38 | callback(err, data, res);
39 | });
40 |
41 | // prepare just before the request is actually sent
42 | var self = this;
43 | ev.addListener('connection', function(opt, conn){
44 | // user agent
45 | if (!('user-agent' in opt.headers))
46 | opt.headers['user-agent'] = 'node-aws';
47 | // sign?
48 | if (self.secret) {
49 | self.signRequest(opt);
50 | }
51 | });
52 |
53 | return ev;
54 | }
55 |
56 |
57 | Bucket.prototype.get = function(path, query, headers, callback){
58 | if (typeof headers === 'function') { callback = headers; headers = undefined; }
59 | else if (typeof query === 'function') { callback = query; query = undefined; }
60 | return this.request({
61 | path: path,
62 | query: query,
63 | headers: headers
64 | }, callback);
65 | }
66 |
67 | Bucket.prototype.put = function(path, data, contentType, options, callback){
68 | if (typeof options === 'function') { callback = options; options = undefined; }
69 | else if (typeof contentType === 'function') { callback = contentType; contentType = undefined; }
70 |
71 | var opt = {
72 | method: 'PUT',
73 | path: path,
74 | body: data,
75 | headers: {}
76 | };
77 |
78 | if (typeof options === 'object')
79 | for (var k in options) opt[k] = options[k];
80 |
81 | if (typeof opt.headers !== 'object')
82 | opt.headers = {};
83 |
84 | if (!('x-amz-acl' in opt.headers))
85 | opt.headers['x-amz-acl'] = 'public-read';
86 |
87 | if (contentType) {
88 | if (typeof opt.headers !== 'object') opt.headers = {};
89 | opt.headers['content-type'] = contentType;
90 | }
91 |
92 | return this.request(opt, callback);
93 | }
94 |
95 | Bucket.prototype.del = function(path, options, callback){
96 | if (typeof options === 'function') { callback = options; options = undefined; }
97 | var opt = {
98 | method: 'DELETE',
99 | path: path
100 | };
101 | if (typeof options === 'object')
102 | for (var k in options) opt[k] = options[k];
103 | return this.request(opt, callback);
104 | }
105 |
106 | // Sign a request
107 | Bucket.prototype.signRequest = function(opt, date){
108 | var date = new Date(),
109 | resource = (opt.path || '/');
110 | if (this.id) resource = "/"+this.id + resource;
111 | opt.headers.date = date.toUTCString();
112 | opt.headers.authorization = 'AWS '+this.key+':'+this.sign(opt.method,
113 | opt.headers['content-md5'], opt.headers['content-type'],
114 | date, opt.headers, resource);
115 | }
116 |
117 | // Create signature
118 | Bucket.prototype.sign = function(verb, contentMD5, contentType, date, amzHeaders, resource){
119 | /*
120 | Example:
121 | Authorization: AWS 0PN5J17HBGZHT7JJ3X82:frJIUN8DYpKDtOLCwo//yllqDzg=
122 |
123 | BNF:
124 | Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
125 |
126 | Signature = Base64( HMAC-SHA1( UTF-8-Encoding-Of( YourSecretAccessKeyID, StringToSign ) ) );
127 |
128 | StringToSign = HTTP-Verb + "\n" +
129 | Content-MD5 + "\n" +
130 | Content-Type + "\n" +
131 | Date + "\n" +
132 | CanonicalizedAmzHeaders +
133 | CanonicalizedResource;
134 |
135 | CanonicalizedResource = [ "/" + Bucket ] +
136 | +
137 | [ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
138 |
139 | CanonicalizedAmzHeaders =
140 | */
141 | var s, amzHeadersIsObj = (typeof amzHeaders === 'object');
142 |
143 | // Format date
144 | if (!date) {
145 | if (amzHeadersIsObj && amzHeaders['x-amz-date']) date = amzHeaders['x-amz-date'];
146 | else if (amzHeadersIsObj && amzHeaders['date']) date = amzHeaders['date'];
147 | else date = (new Date()).toUTCString();
148 | } else if (typeof date === 'object') {
149 | date = date.toUTCString();
150 | }
151 |
152 | // Fix headers
153 | if (amzHeadersIsObj) {
154 | s = '';
155 | for (var k in amzHeaders) {
156 | if (k.indexOf('x-amz-') === 0) s += k+':'+amzHeaders[k]+'\n';
157 | }
158 | amzHeaders = s;
159 | } else {
160 | amzHeaders = '\n';
161 | }
162 |
163 | s = verb+"\n"+
164 | (contentMD5 || '')+"\n"+
165 | (contentType || '')+"\n"+
166 | date+"\n"+
167 | amzHeaders+
168 | resource;
169 | //sys.error(s.replace(/\n/gm, '\\n\n'));
170 | return sha1.b64_hmac_sha1(this.secret, s);
171 | }
172 |
173 | /*var b = new Bucket('static.dropular.net',
174 | 'key', 'secret');
175 |
176 | b.put('/test1.txt', 'hello test1', 'text/plain', function(err, data, res){
177 | if (err) throw err;
178 | b.get('/test1.txt', function(err, data, res){
179 | if (err) throw err;
180 | b.del('/test1.txt', function(err, data, res){
181 | if (err) throw err;
182 | })
183 | })
184 | })
185 | */
186 |
--------------------------------------------------------------------------------
/lib/aws/sha1.js:
--------------------------------------------------------------------------------
1 | /*
2 | * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
3 | * in FIPS PUB 180-1
4 | * Version 2.1a Copyright Paul Johnston 2000 - 2002.
5 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
6 | * Distributed under the BSD License
7 | * See http://pajhome.org.uk/crypt/md5 for details.
8 | */
9 |
10 | /*
11 | * Configurable variables. You may need to tweak these to be compatible with
12 | * the server-side, but the defaults work in most cases.
13 | */
14 | var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
15 | var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */
16 | var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
17 |
18 | /*
19 | * These are the functions you'll usually want to call
20 | * They take string arguments and return either hex or base-64 encoded strings
21 | */
22 | exports.hex_sha1 = function (s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
23 | exports.b64_sha1 = function (s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
24 | exports.str_sha1 = function (s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
25 | exports.hex_hmac_sha1 = function (key, data){ return binb2hex(core_hmac_sha1(key, data));}
26 | exports.b64_hmac_sha1 = function (key, data){ return binb2b64(core_hmac_sha1(key, data));}
27 | exports.str_hmac_sha1 = function (key, data){ return binb2str(core_hmac_sha1(key, data));}
28 |
29 | /*
30 | * Perform a simple self-test to see if the VM is working
31 | */
32 | function sha1_vm_test()
33 | {
34 | return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
35 | }
36 |
37 | /*
38 | * Calculate the SHA-1 of an array of big-endian words, and a bit length
39 | */
40 | function core_sha1(x, len)
41 | {
42 | /* append padding */
43 | x[len >> 5] |= 0x80 << (24 - len % 32);
44 | x[((len + 64 >> 9) << 4) + 15] = len;
45 |
46 | var w = Array(80);
47 | var a = 1732584193;
48 | var b = -271733879;
49 | var c = -1732584194;
50 | var d = 271733878;
51 | var e = -1009589776;
52 |
53 | for(var i = 0; i < x.length; i += 16)
54 | {
55 | var olda = a;
56 | var oldb = b;
57 | var oldc = c;
58 | var oldd = d;
59 | var olde = e;
60 |
61 | for(var j = 0; j < 80; j++)
62 | {
63 | if(j < 16) w[j] = x[i + j];
64 | else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
65 | var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
66 | safe_add(safe_add(e, w[j]), sha1_kt(j)));
67 | e = d;
68 | d = c;
69 | c = rol(b, 30);
70 | b = a;
71 | a = t;
72 | }
73 |
74 | a = safe_add(a, olda);
75 | b = safe_add(b, oldb);
76 | c = safe_add(c, oldc);
77 | d = safe_add(d, oldd);
78 | e = safe_add(e, olde);
79 | }
80 | return Array(a, b, c, d, e);
81 |
82 | }
83 |
84 | /*
85 | * Perform the appropriate triplet combination function for the current
86 | * iteration
87 | */
88 | function sha1_ft(t, b, c, d)
89 | {
90 | if(t < 20) return (b & c) | ((~b) & d);
91 | if(t < 40) return b ^ c ^ d;
92 | if(t < 60) return (b & c) | (b & d) | (c & d);
93 | return b ^ c ^ d;
94 | }
95 |
96 | /*
97 | * Determine the appropriate additive constant for the current iteration
98 | */
99 | function sha1_kt(t)
100 | {
101 | return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
102 | (t < 60) ? -1894007588 : -899497514;
103 | }
104 |
105 | /*
106 | * Calculate the HMAC-SHA1 of a key and some data
107 | */
108 | function core_hmac_sha1(key, data)
109 | {
110 | var bkey = str2binb(key);
111 | if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
112 |
113 | var ipad = Array(16), opad = Array(16);
114 | for(var i = 0; i < 16; i++)
115 | {
116 | ipad[i] = bkey[i] ^ 0x36363636;
117 | opad[i] = bkey[i] ^ 0x5C5C5C5C;
118 | }
119 |
120 | var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
121 | return core_sha1(opad.concat(hash), 512 + 160);
122 | }
123 |
124 | /*
125 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally
126 | * to work around bugs in some JS interpreters.
127 | */
128 | function safe_add(x, y)
129 | {
130 | var lsw = (x & 0xFFFF) + (y & 0xFFFF);
131 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
132 | return (msw << 16) | (lsw & 0xFFFF);
133 | }
134 |
135 | /*
136 | * Bitwise rotate a 32-bit number to the left.
137 | */
138 | function rol(num, cnt)
139 | {
140 | return (num << cnt) | (num >>> (32 - cnt));
141 | }
142 |
143 | /*
144 | * Convert an 8-bit or 16-bit string to an array of big-endian words
145 | * In 8-bit function, characters >255 have their hi-byte silently ignored.
146 | */
147 | function str2binb(str)
148 | {
149 | var bin = new Array();
150 | var mask = (1 << chrsz) - 1;
151 | for(var i = 0; i < str.length * chrsz; i += chrsz) {
152 | bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
153 | }
154 | return bin;
155 | }
156 |
157 | /*
158 | * Convert an array of big-endian words to a string
159 | */
160 | function binb2str(bin)
161 | {
162 | var str = "";
163 | var mask = (1 << chrsz) - 1;
164 | for(var i = 0; i < bin.length * 32; i += chrsz)
165 | str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
166 | return str;
167 | }
168 |
169 | /*
170 | * Convert an array of big-endian words to a hex string.
171 | */
172 | function binb2hex(binarray)
173 | {
174 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
175 | var str = "";
176 | for(var i = 0; i < binarray.length * 4; i++)
177 | {
178 | str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
179 | hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
180 | }
181 | return str;
182 | }
183 |
184 | /*
185 | * Convert an array of big-endian words to a base-64 string
186 | */
187 | function binb2b64(binarray)
188 | {
189 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
190 | var str = "";
191 | for(var i = 0; i < binarray.length * 4; i += 3)
192 | {
193 | var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
194 | | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
195 | | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
196 | for(var j = 0; j < 4; j++)
197 | {
198 | if((i * 8 + j * 6) > (binarray.length * 32)) str += b64pad;
199 | else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
200 | }
201 | }
202 | return str;
203 | }
204 |
--------------------------------------------------------------------------------
/misc/docs/setting-up-an-ec2-instance.md:
--------------------------------------------------------------------------------
1 | # Setting up an EC2 instance
2 |
3 | ## Prerequisites
4 |
5 | - [Amazon EC2 command line tools](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=351&categoryID=88)
6 |
7 | It is recommended to put the contents of the zip file referenced in the above web page in `~/.ec2`. First, unzip the file, then:
8 |
9 | mv ec2-api-tools-1.3-46266 ~/.ec2
10 | chmod 0700 ~/.ec2
11 |
12 | Now, you need to add the tools to your `PATH`. Edit your `~/.bashrc` file and add these lines somewhere:
13 |
14 | if [ -d ~/.ec2 ]; then
15 | export EC2_HOME="$HOME/.ec2"
16 | export PATH=$PATH:$EC2_HOME/bin
17 | export EC2_PRIVATE_KEY=`ls $EC2_HOME/pk-*.pem`
18 | export EC2_CERT=`ls $EC2_HOME/cert-*.pem`
19 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home/
20 | fi
21 |
22 | Download the X.509 certificates from the Amazon AWS accounts page and put them into `~/.ec2` and secure permissions (`chmod 0400 ~/.ec2/*.pem`).
23 |
24 | Now, listing the contents of `~/.ec2` should now yield something like this:
25 |
26 | $ ls -l ~/.ec2
27 | drwxrwxr-x 352 rasmus staff 11968 13 Dec 22:25 bin
28 | -r-------- 1 rasmus staff 916 17 Apr 13:56 cert-xxxxxxxxx.pem
29 | -r-------- 1 rasmus staff 1693 17 Apr 13:31 dropular-ec2-1.pem
30 | drwxrwxr-x 26 rasmus staff 884 13 Dec 22:25 lib
31 | -r-------- 1 rasmus staff 922 17 Apr 13:56 pk-xxxxxxxxx.pem
32 |
33 | > **IMPORTANT --** THE `pem` FILES ARE CONFIDENTIAL AND SHALL NEVER FALL INTO THE HANDS OF OTHER PEOPLE, as they are the key to controlling anything.
34 |
35 | ## Creating and configuring a new EC2 instance
36 |
37 | Choose an instance id. This should be in the form "dropular-ec2-N" where "N" is the next natural number:
38 |
39 | INSTANCEID='dropular-ec2-2'
40 |
41 | Now, create a key pair and launch a new instance with that key pair:
42 |
43 | mkdir -p ~/.ec2 && chmod 0700 ~/.ec2
44 | ec2-add-keypair $INSTANCEID > ~/.ec2/$INSTANCEID.pem
45 | chmod 0400 ~/.ec2/$INSTANCEID.pem
46 | ec2-run-instances ami-19a34270 -k $INSTANCEID
47 |
48 | > `ami-19a34270` is a 32bit Ubuntu 9.10 "karmic" image from Alestic for the Virigina site. `ami-2fc2e95b` is an alternative 32bit Ubuntu 9.10 "karmic" image for the EU site.
49 |
50 | Create a SSH short-hand alias:
51 |
52 | HOSTNAME=$(ec2-describe-instances | grep $INSTANCEID | awk '{print $4}')
53 | echo "Host $INSTANCEID" >> ~/.ssh/config
54 | echo " HostName $HOSTNAME" >> ~/.ssh/config
55 | echo " User root" >> ~/.ssh/config
56 | echo " IdentityFile $HOME/.ec2/$INSTANCEID.pem" >> ~/.ssh/config
57 | chmod 0600 ~/.ssh/config
58 |
59 | Also, make sure the security group in which the instance is operating in (`default` by default) have the appropriate ports opened:
60 |
61 | ec2-authorize default -p 22
62 | ec2-authorize default -p 80
63 | ec2-authorize default -p 8100-8199
64 |
65 | Now, you can log in to the server by referencing the `$INSTANCEID`. E.g:
66 |
67 | ssh $INSTANCEID
68 |
69 | > If the instance was started in a special region, include the `--region` option when calling the ec2 commands. E.g. `ec2-authorize --region eu-west-1 default -p 22`
70 |
71 | ## Setting up the system
72 |
73 | > If you see lines like this `E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem.` -- simply run `dpkg --configure -a` and re-run `apt-get install x` until completed. This is a EC2 specific issue.
74 |
75 | apt-get update
76 | apt-get install build-essential libc6-dev libstdc++6 git-core
77 | mkdir -p ~/src
78 |
79 | **Node:**
80 |
81 | cd ~/src
82 | git clone git://github.com/ry/node.git
83 | cd node && git checkout v0.1.94
84 | ./configure && make && make install
85 |
86 | **CouchDB:**
87 |
88 | apt-get install erlang libicu-dev libmozjs-dev libcurl4-openssl-dev
89 | cd ~/src
90 | wget http://www.apache.org/dist/couchdb/0.11.0/apache-couchdb-0.11.0.tar.gz
91 | tar xfz apache-couchdb-0.11.0.tar.gz && cd apache-couchdb-0.11.0
92 | adduser --system --home /var/lib/couchdb --no-create-home\
93 | --shell /bin/bash --group --gecos 'CouchDB Administrator' couchdb
94 | ./configure --prefix ''
95 | make && make install
96 | update-rc.d couchdb defaults
97 | invoke-rc.d couchdb start
98 |
99 | **Dropular user:**
100 |
101 | adduser --system --home /var/dropular --shell /bin/bash --group\
102 | --gecos 'Dropular system user' dropular
103 |
104 | **Git deploy key:**
105 |
106 | Chose all default answers asked by `ssh-keygen`
107 |
108 | sudo su dropular
109 | ssh-keygen
110 | less ~/.ssh/id_rsa.pub
111 |
112 | Copy the output from less and create a new deploy key at [https://github.com/rsms/dropular/edit](https://github.com/rsms/dropular/edit) -- name it `$INSTANCEID` (e.g. "dropular-ec2-2").
113 |
114 | **`/var/dropular`:**
115 |
116 | apt-get install daemon imagemagick
117 | sudo su dropular
118 | cd
119 | git clone git://github.com/rsms/oui.git && oui/client/build.sh -s
120 | git clone git://github.com/rsms/node-couchdb-min.git
121 | git clone git://github.com/rsms/node-imagemagick.git
122 | git clone git@github.com:rsms/dropular.git && dropular/build.sh -s
123 | # ^D -- log out dropular and be logged in as root
124 | ln -s /var/dropular/dropular/misc/init.d/dropular-httpd /etc/init.d/
125 | update-rc.d dropular-httpd defaults
126 | $EDITOR /etc/default/dropular-httpd
127 | # enter something like: DR_HTTPD_PORTS="8100 8101"
128 | invoke-rc.d dropular-httpd start
129 |
130 | **nginx:**
131 |
132 | apt-get install nginx
133 | cd /etc/nginx/sites-enabled
134 | cp /var/dropular/dropular/misc/nginx.conf ../sites-available/dropular
135 | ln -s ../sites-available/dropular
136 | $EDITOR dropular
137 | # edit dropular_backends to match DR_HTTPD_PORTS in /etc/defaults/dropular-httpd
138 | invoke-rc.d nginx restart
139 |
140 | ## Setting up the data
141 |
142 | First, create a temporary tunnel to the instance from which you want to replicate (while being logged in on the EC2 instance):
143 |
144 | ssh -L5984:127.0.0.1:5984 rasmus@hunch.se
145 |
146 | In a new terminal (logged in to the EC2 instance):
147 |
148 | curl -X PUT http://127.0.0.1:5984/dropular-{users,drops,newslist}
149 | curl -vX POST http://127.0.0.1:5984/_replicate -d\
150 | '{"source":"http://127.0.0.1:5985/dropular-drops","target":"dropular-drops"}'
151 | curl -vX POST http://127.0.0.1:5984/_replicate -d\
152 | '{"source":"http://127.0.0.1:5985/dropular-users","target":"dropular-users"}'
153 |
154 | This will take some time, depending on the load of the source instance and the amount of data (normally about 10 minutes).
155 |
156 | ## Testing the dropular server
157 |
158 | cd /var/dropular/dropular
159 | ./build.sh -s
160 | ./httpd.js -d
161 |
162 |
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | if (window.OUI_HELP) {
2 | // Extend the message from help
3 | window.OUI_HELP.sections.Examples +=
4 | "\n"+
5 | " Creating or updating a drop:\n"+
6 | " oui.app.session.post('drop', {'url':'http://www.com/some/image.jpg'},\n"+
7 | " function(err, result, resp) { console.log(err, result, resp); });\n";
8 | }
9 |
10 | // BEGIN TEMPORARY FIX
11 | if (!oui.cookie.get('amazonfuckedupourserver')) {
12 | oui.cookie.clear('auth_token');
13 | oui.cookie.set('amazonfuckedupourserver', 'ohhowsad', 60*60*24*365*10);
14 | }
15 | // END TEMPORARY FIX
16 |
17 | exports.reloadUser = function(callback){
18 | if (oui.app.session.user) {
19 | console.log(__name+': reloading user '+oui.app.session.user.username);
20 | users.User.find(oui.app.session.user.username, function(err, user){
21 | if (err) {
22 | util.notify.show(err);
23 | } else {
24 | console.log(__name+': reloaded user '+(user ? user.username : ''));
25 | oui.app.session.setUser(user);
26 | }
27 | if (callback) callback(err);
28 | });
29 | } else {
30 | if (callback) callback();
31 | }
32 | };
33 |
34 | exports.updateUserLevelStyle = function(user) {
35 | user = user || oui.app.session.user;
36 | // lazily create the style tag, controlling visibility
37 | if (!exports.$userLevelStyle) {
38 | var $head = $('html > head');
39 | $head.append('');
40 | exports.$userLevelStyle = $head.find('style:last');
41 | }
42 | // Hide/show any elements according to user's level
43 | var s = [], i, levels = 3, level = levels;
44 | if (user && typeof user.level === 'number')
45 | level = user.level;
46 | // refresh trick -- might be needed for legacy browsers. Let's keep it for now.
47 | for (i=levels; --i >= level; ) {
48 | s.push('.userlevel'+i+' { display:block; }');
49 | }
50 | for (i=level; --i > -1; ) {
51 | s.push('.userlevel'+i+' { display:none; }');
52 | }
53 | for (i=0;i*{outline:none;}');
69 |
70 | // update universal UI stuff on userchange
71 | oui.app.session.on('userchange', function(ev, prevUser){
72 | exports.updateUserLevelStyle(this.user);
73 |
74 | if (this.user) {
75 | $('.following').css('display','block');
76 | } else{
77 | $('.following').css('display','none');
78 | }
79 |
80 | if (!this.user && prevUser && document.location.hash === '#drops') {
81 | // when a user logs out while at a user-restricted view, send her to home:
82 | $('.following').css('display','none');
83 | document.location.hash = '#';
84 | }
85 | $('grid').delay(200).show();
86 | });
87 | });
88 |
89 | // Scroll state persistence
90 | var getScroll;
91 | if (document.all) {
92 | getScroll = function() {
93 | return { x: document.scrollLeft, y: document.scrollTop };
94 | };
95 | } else {
96 | getScroll = function() {
97 | return { x: window.pageXOffset, y: window.pageYOffset };
98 | };
99 | }
100 | // keyed by document.location.hash
101 | exports.scrollStates = {};
102 | oui.anchor.events.addListener('change', function(ev, path, prevPath, routes){
103 | exports.scrollStates[prevPath] = getScroll();
104 | });
105 | oui.anchor.events.addListener('changed', function(ev, path, prevPath, routes){
106 | var scrollState = exports.scrollStates[path];
107 | if (scrollState) {
108 | exports.scrollStates[path] = undefined;
109 | oui.app.session.on('idle', true, function(){
110 | setTimeout(function(){
111 | console.debug('restoring scroll to', scrollState.x, scrollState.y);
112 | window.scrollTo(scrollState.x, scrollState.y);
113 | },500); // todo: this is a shaky solution. must be some event we can listen for...
114 | });
115 | }
116 | });
117 |
118 |
119 | exports.mixinViewControl = function (exports, selector) {
120 | $(document).ready(function(){
121 | exports.$container = $(selector);
122 |
123 | $('#taglookup input').focus(function() {
124 | $("#taglookup input").val("");
125 | });
126 |
127 | $('#taglookup').submit(function() {
128 | if ($("#taglookup input").val()) {
129 | to_url = window.location.href.replace( /#.*/, "")+'#drops/tagged/'+encodeURIComponent($("#taglookup input").val());
130 | document.location = to_url;
131 | return false;
132 | }
133 | return false;
134 | });
135 | });
136 |
137 |
138 | exports.events = new oui.EventEmitter();
139 |
140 | exports.visible = function(view){
141 | if (view) {
142 | return (exports.$container.has(view).length !== 0
143 | && view.css('display') !== 'none'
144 | && view.css('opacity') !== '0');
145 | } else {
146 | return (exports.$container.children().length !== 0
147 | && exports.$container.css('display') !== 'none'
148 | && exports.$container.css('opacity') !== '0');
149 | }
150 | };
151 |
152 | exports.clear = function(callback) {
153 | if (exports.visible) {
154 | exports.events.emit('viewclear');
155 | exports.events.emit('viewchange');
156 | exports.$container.fadeOut(200, callback);
157 | }
158 | };
159 |
160 | exports.title = function(title) {
161 | var $h1 = exports.$container.find('h1'), currTitle = $h1.text();
162 | if (title)
163 | $h1.text(title);
164 | return currTitle;
165 | };
166 |
167 | exports.viewQueuedForDisplay = null;
168 |
169 | exports.setView = function(view, callback, title){
170 | if (typeof view === 'function') {
171 | callback = view;
172 | view = null;
173 | }
174 |
175 | exports.events.emit('viewchange');
176 | if (!view) {
177 | exports.$container.fadeIn(200, callback);
178 | } else if (!exports.visible(view)) {
179 | exports.viewQueuedForDisplay = view;
180 | exports.$container.fadeOut(100, function(){
181 | if (view === exports.viewQueuedForDisplay) {
182 | exports.viewQueuedForDisplay = null;
183 | // defer to next tick, since otherwise the object is display:none
184 | // and things like input focus will not work.
185 | setTimeout(function(){
186 | (view.ouiModule ? view.ouiModule.$html : view).triggerHandler('load', view);
187 | if (view.ouiModule) view.ouiModule.emit('load', view);
188 | },1);
189 | exports.$container.empty().append(view.show()).fadeIn(200, callback);
190 | }
191 | });
192 | } else if (callback) {
193 | callback();
194 | }
195 | };
196 | };
197 |
198 |
--------------------------------------------------------------------------------
/misc/init.d/dropular-httpd:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | ### BEGIN INIT INFO
3 | # Provides: dropular-httpd
4 | # Required-Start: $remote_fs
5 | # Required-Stop: $remote_fs
6 | # Default-Start: 2 3 4 5
7 | # Default-Stop: 0 1 6
8 | # Short-Description: Dropular HTTP server
9 | # Description: Dropular HTTP server
10 | ### END INIT INFO
11 |
12 | # Author: Rasmus Andersson
13 | # Do NOT "set -e"
14 |
15 | # PATH should only include /usr/* if it runs after the mountnfs.sh script
16 | PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
17 | NAME=dropular-httpd
18 | DESC="Dropular HTTP server"
19 | RUNDIR=/var/run/dropular
20 | LOGDIR=/var/log/dropular
21 | SCRIPTNAME=/etc/init.d/$NAME
22 | USER=dropular
23 | GROUP=dropular
24 |
25 | # Default configuration for dropular-httpd
26 | DR_HTTPD_BIN=/var/dropular/dropular/httpd.js
27 | # Start one instance per port
28 | #DR_HTTPD_PORTS="8100 8101 8102 8103"
29 | DR_HTTPD_PORTS=8100
30 | #DR_HTTPD_ARGS='--debug'
31 | # How long to sleep in between instance restarts
32 | DR_HTTPD_RESTART_SLEEP=1
33 |
34 | # Daemon supervisor config
35 | SUPERVISOR=/usr/bin/daemon
36 | SUPERVISOR_ARGS="--respawn --chdir=$(dirname $DR_HTTPD_BIN)" # do NOT set --user here
37 | SUPERVISOR_PIDDIR="$RUNDIR"
38 |
39 | # Exit if the package is not available
40 | test -f "$DR_HTTPD_BIN" || exit 0
41 |
42 | # Read configuration variable file if it is present
43 | [ -r /etc/default/$NAME ] && . /etc/default/$NAME
44 |
45 | # The following are for internal use
46 | DR_HTTPD_NPORTS=$(echo $DR_HTTPD_PORTS | wc -w)
47 |
48 | # Load the VERBOSE setting and other rcS variables
49 | . /lib/init/vars.sh
50 |
51 | # Define LSB log_* functions.
52 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
53 | . /lib/lsb/init-functions
54 |
55 | # Make sure run and log directories exist
56 | mkdir -p $RUNDIR > /dev/null 2> /dev/null
57 | chown -R $USER:$GROUP $RUNDIR
58 | chmod 0750 $RUNDIR
59 | mkdir -p $LOGDIR > /dev/null 2> /dev/null
60 | chown -R $USER:$GROUP $LOGDIR
61 | chmod 0750 $LOGDIR
62 |
63 | VERBOSE=yes
64 |
65 | # start_instance PORT
66 | start_instance() {
67 | DR_HTTPD_PORT=$1
68 | INSTANCE_ID="$NAME-$DR_HTTPD_PORT"
69 | PIDFILE="$SUPERVISOR_PIDDIR/$INSTANCE_ID.pid"
70 | [ "$VERBOSE" != no ] && log_daemon_msg " starting $INSTANCE_ID"
71 | start-stop-daemon --start --quiet --pidfile $PIDFILE \
72 | --exec $SUPERVISOR --test > /dev/null
73 | STATUS="$?"
74 | if [ "$STATUS" = "0" ]; then
75 | start-stop-daemon --start --quiet --pidfile $PIDFILE \
76 | --exec $SUPERVISOR --chuid $USER:$GROUP -- \
77 | $SUPERVISOR_ARGS \
78 | --name=$INSTANCE_ID --pidfile=$PIDFILE \
79 | --stdout=$LOGDIR/$INSTANCE_ID.out \
80 | --errlog=$LOGDIR/$INSTANCE_ID.err \
81 | -- "$DR_HTTPD_BIN" $DR_HTTPD_ARGS --port $DR_HTTPD_PORT
82 | STATUS="$?"
83 | else
84 | STATUS=2
85 | fi
86 | case "$STATUS" in
87 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
88 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
89 | esac
90 | return $STATUS
91 | }
92 |
93 | # stop_instance PORT
94 | stop_instance() {
95 | DR_HTTPD_PORT=$1
96 | INSTANCE_ID="$NAME-$DR_HTTPD_PORT"
97 | PIDFILE="$SUPERVISOR_PIDDIR/$INSTANCE_ID.pid"
98 | [ "$VERBOSE" != no ] && log_daemon_msg " stopping $INSTANCE_ID"
99 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
100 | STATUS="$?"
101 | case "$STATUS" in
102 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
103 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
104 | esac
105 | if [ "$STATUS" != "2" ]; then
106 | rm -f $PIDFILE
107 | fi
108 | return $STATUS
109 | }
110 |
111 | # signal_instance PORT SIGNAL
112 | signal_instance() {
113 | DR_HTTPD_PORT=$1
114 | SIGNAL=$2
115 | INSTANCE_ID="$NAME-$DR_HTTPD_PORT"
116 | PIDFILE="$SUPERVISOR_PIDDIR/$INSTANCE_ID.pid"
117 | [ "$VERBOSE" != no ] && log_daemon_msg " sending signal $SIGNAL to $INSTANCE_ID"
118 | kill -s $SIGNAL $(cat $PIDFILE)
119 | case "$?" in
120 | 0) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
121 | *) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
122 | esac
123 | return $?
124 | }
125 |
126 | do_start() {
127 | # Return
128 | # 0 if daemon has been started
129 | # 1 if daemon was already running
130 | # 2 if daemon could not be started
131 | for DR_HTTPD_PORT in $DR_HTTPD_PORTS; do
132 | start_instance $DR_HTTPD_PORT
133 | done
134 | return 0
135 | }
136 |
137 | do_stop() {
138 | # Return
139 | # 0 if daemon has been stopped
140 | # 1 if daemon was already stopped
141 | # 2 if daemon could not be stopped
142 | # other if a failure occurred
143 | RETVAL="0"
144 | for DR_HTTPD_PORT in $DR_HTTPD_PORTS; do
145 | stop_instance $DR_HTTPD_PORT
146 | if [ "$?" = "2" ] ; then RETVAL="2" ; fi
147 | done
148 | return "$RETVAL"
149 | }
150 |
151 | do_restart() {
152 | NPORT=0
153 | for DR_HTTPD_PORT in $DR_HTTPD_PORTS; do
154 | NPORT=$(expr $NPORT + 1)
155 | stop_instance $DR_HTTPD_PORT
156 | start_instance $DR_HTTPD_PORT
157 | [ "$?" = 2 ] && return 2
158 | if [ $DR_HTTPD_RESTART_SLEEP -gt 0 ] && [ $NPORT -lt $DR_HTTPD_NPORTS ]; then
159 | [ "$VERBOSE" != no ] && log_action_msg " waiting $DR_HTTPD_RESTART_SLEEP second(s) until restarting next instance..."
160 | sleep $DR_HTTPD_RESTART_SLEEP
161 | fi
162 | done
163 | return 0
164 | }
165 |
166 | do_reload() {
167 | for DR_HTTPD_PORT in $DR_HTTPD_PORTS; do
168 | signal_instance $DR_HTTPD_PORT 1
169 | [ "$?" = 2 ] && return 2
170 | done
171 | return 0
172 | }
173 |
174 | case "$1" in
175 | start)
176 | [ "$VERBOSE" != no ] && log_action_msg "Starting $DESC"
177 | do_start
178 | case "$?" in
179 | 0|1) [ "$VERBOSE" != no ] && log_action_msg "Started $DR_HTTPD_NPORTS instances" ;;
180 | 2) [ "$VERBOSE" != no ] && log_action_msg "Failed to start all $DR_HTTPD_NPORTS instances" ;;
181 | esac
182 | ;;
183 | stop)
184 | [ "$VERBOSE" != no ] && log_action_msg "Stopping $DESC"
185 | do_stop
186 | case "$?" in
187 | 0|1) [ "$VERBOSE" != no ] && log_action_msg "Stopped $DR_HTTPD_NPORTS instances" ;;
188 | 2) [ "$VERBOSE" != no ] && log_action_msg "Failed to stop all $DR_HTTPD_NPORTS instances" ;;
189 | esac
190 | ;;
191 | restart|force-reload)
192 | [ "$VERBOSE" != no ] && log_action_msg "Restarting $DESC"
193 | do_restart
194 | case "$?" in
195 | 0|1) [ "$VERBOSE" != no ] && log_action_msg "Restarted $DR_HTTPD_NPORTS instances" ;;
196 | 2) [ "$VERBOSE" != no ] && log_action_msg "Failed to restart all $DR_HTTPD_NPORTS instances" ;;
197 | esac
198 | ;;
199 | reload)
200 | [ "$VERBOSE" != no ] && log_action_msg "Reloading $DESC"
201 | do_reload
202 | case "$?" in
203 | 0|1) [ "$VERBOSE" != no ] && log_action_msg "Reloaded $DR_HTTPD_NPORTS instances" ;;
204 | 2) [ "$VERBOSE" != no ] && log_action_msg "Failed to reload all $DR_HTTPD_NPORTS instances" ;;
205 | esac
206 | ;;
207 | *)
208 | echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
209 | exit 3
210 | ;;
211 | esac
212 |
213 | :
214 |
--------------------------------------------------------------------------------
/client/about/terms.html:
--------------------------------------------------------------------------------
1 |
4 |
41 |
42 |
43 |
Terms of service
44 |
45 |
46 | By accessing the Dropular.net website ("Site") or using the services offered by Dropular.net ("Services") you agree and acknowledge to be bound by these Terms of Service ("Terms"). If you do not agree to these Terms or to our Privacy Policy, please do not access the Site or use the Services. Dropular.net reserves the right to change these Terms at any time. We recommend that you periodically check this Site for changes.
47 |
48 |
49 |
User content posted on the site
50 |
51 | You own all of the content and information you post on Dropular.net. In addition, for content that is covered by intellectual property rights, like photos, you specifically give us the following permission: you grant us a non-exclusive, transferable, sub-licensable, royalty-free, worldwide license to use any IP content that you post on or in connection with Dropular.net ("IP License"). This IP License ends when you delete your IP content or your account.
52 |
53 |
54 |
Prohibited uses
55 |
56 | You may not use the Dropular.net site and or its services to transmit any content which:
57 |
58 |
59 |
60 |
harasses, threatens, embarrasses or causes distress, unwanted attention or discomfort upon any other person,
61 |
includes sexually explicit images or other content which is offensive or harmful to minors,
62 |
includes any unlawful, harmful, threatening, abusive, harassing, defamatory, vulgar, obscene, or hateful material, including but not limited to material based on a person's race, national origin, ethnicity, religion, gender, sexual orientation, disablement or other such affiliation,
63 |
impersonates any person or the appearance or voice of any person,
64 |
utilizes a false name or identity or a name or identity that you are not entitled or authorized to use,
65 |
contains any unsolicited advertising, promotional materials, or other forms of solicitation,
66 |
67 |
contravenes any application law or government regulation,
68 |
violates any operating rule, regulation, procedure, policy or guideline of Dropular.net as published on the Dropular.net website,
69 |
may infringe the intellectual property rights or other rights of third parties, including trademark, copyright, trade secret, patent, publicity right, or privacy right, or
70 |
distributes software or other Content in violation of any license agreement.
71 |
72 |
73 |
Use of content on external sites
74 |
75 |
76 | You understand that if you use a Dropular.net photo on an external web site, then the photo must link back to its look detail page on Dropular.net.
77 |
78 |
79 |
80 |
No warranty and limitation of liability
81 |
82 | Dropular.net PROVIDES THE SITE AND SERVICES "AS IS" AND WITHOUT ANY WARRANTY OR CONDITION, EXPRESS, IMPLIED OR STATUTORY. Dropular.net SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, INFORMATION ACCURACY, INTEGRATION, INTEROPERABILITY OR QUIET ENJOYMENT. Some states do not allow the disclaimer of implied warranties, so the foregoing disclaimer may not apply to you.
83 |
84 |
85 | You understand and agree that you use the Site and Services at your own discretion and risk and that you will be solely responsible for any damages that arise from such use. UNDER NO CIRCUMSTANCES SHALL Dropular.net BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES OF ANY KIND, OR ANY OTHER DAMAGES WHATSOEVER (HOWEVER ARISING, INCLUDING BY NEGLIGENCE), INCLUDING WITHOUT LIMITATION, DAMAGES RELATED TO USE, MISUSE, RELIANCE ON, INABILITY TO USE AND INTERRUPTION, SUSPENSION, OR TERMINATION OF THE SITE OR SERVICES, DAMAGES INCURRED THROUGH ANY LINKS PROVIDED ON THE SITE AND THE NONPERFORMANCE THEREOF AND DAMAGES RESULTING FROM LOSS OF USE, SALES, DATA, GOODWILL OR PROFITS, WHETHER OR NOT Dropular.net HAS BEEN ADVISED OF SUCH POSSIBILITY. YOUR ONLY RIGHT WITH RESPECT TO ANY DISSATISFACTION WITH THIS SITE OR SERVICES OR WITH Dropular.net SHALL BE TO TERMINATE USE OF THIS SITE AND SERVICES. Some states do not allow the exclusion of liability for incidental or consequential damages, so the above exclusions may not apply to you.
86 |
87 |
88 |
Other
89 |
90 |
91 | Dropular.net, in its sole discretion, may terminate your membership and remove and discard any information associated with the membership with or without notice. Dropular.net will not be liable to you for termination of your membership to the Service.
92 |
93 |
94 | Dropular.net, in its sole discretion, may delete any of the content posted to the Site and remove and discard any information associated with the content with or without notice. Dropular.net will not be liable to you for deletion of the images.
95 |
96 |
97 | Dropular.net reserves the right at any time and from time to time to modify or discontinue, temporarily or permanently, the Service (or any part thereof) with or without notice. You agree that Dropular.net shall not be liable to you or to any third party for any modification, suspension or discontinuance of the Service.
98 |
99 | Dropular.net Terms of Service as noted above may be updated by us from time to time without notice to you. In addition, when using particular Dropular.net owned or operated services, you and Dropular.net shall be subject to any posted guidelines or rules applicable to such services, which may be posted from time to time.
100 |