commit ab33eee5799ba6d038f101e5d00de0a48fece5b7 Author: AJ ONeal Date: Tue Jul 21 18:58:34 2015 -0600 partial work diff --git a/README.md b/README.md new file mode 100644 index 0000000..86ede55 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +DRAFT +===== + +This is just hypothetical while I build out the API + +SQLite3 Server +============= + +Node.js runs on a single core, which isn't very effective. + +You can run multiple Node.js instances to take advantage of multiple cores, +but if you do that, you can't use SQLite in each process. + +This module will either run client-server style in environments that benefit from it +(such as the Raspberry Pi 2 with 4 cores), or in-process for environments that don't +(such as the Raspberry Pi B and B+). + +Usage +===== + +```js +var sqlite = require('sqlite3-server'); +var opts = { + key: '1892d335081d8d346e556c9c3c8ff2c3' +, bits: 128 +, storage: path.join('/tmp/authn.sqlcipher') +, verbose: false +, port: 3232 // default random +, forceServer: true // default false +}; + +sqlite.create(opts).then(function (db) { + // EXACT same api as db +}); +``` diff --git a/client.js b/client.js new file mode 100644 index 0000000..429d26d --- /dev/null +++ b/client.js @@ -0,0 +1,3 @@ +'use strict'; + + diff --git a/index.js b/index.js new file mode 100644 index 0000000..7666bba --- /dev/null +++ b/index.js @@ -0,0 +1,12 @@ +'use strict'; + +var path = require('path'); +var numcpus = require('os').cpus().length; + +if (numcpus >= 2) { + sqlite3 = require('./sqlite-client'); +} else { + sqlite3 = require('./sqlite3-wrapper'); +} + +module.exports = sqlite3; diff --git a/install-sqlcipher.bash b/install-sqlcipher.bash new file mode 100644 index 0000000..851ea4d --- /dev/null +++ b/install-sqlcipher.bash @@ -0,0 +1,11 @@ +#brew options sqlcipher +#brew install sqlcipher --with-fts +echo STOP +echo You must manually install sqlcipher +exit 1 + +export LDFLAGS="-L`brew --prefix`/opt/sqlcipher/lib" +export CPPFLAGS="-I`brew --prefix`/opt/sqlcipher/include" +npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=`brew --prefix` + +node -e 'require("sqlite3")' diff --git a/serve.js b/serve.js new file mode 100644 index 0000000..9049eb2 --- /dev/null +++ b/serve.js @@ -0,0 +1,15 @@ +'use strict'; + +var server = require('http').createServer(); +var WebSocketServer = require('ws').Server; +var wss = new WebSocketServer({ server: server }); +var port = process.env.PORT || process.argv[0] || 4080; + +var app = require('./sqlite-server'); + +server.listen(port, function () { +}); + +app.create({ server: server, wss: wss }).then(function (app) { + server.on('request', app); +}); diff --git a/server.js b/server.js new file mode 100644 index 0000000..8829198 --- /dev/null +++ b/server.js @@ -0,0 +1,55 @@ +'use strict'; + +function create(options) { + var url = require('url'); + var express = require('express'); + var app = express(); + var wss = options.wss; + + wss.on('connection', function (ws) { + var location = url.parse(ws.upgradeReq.url, true); + // you might use location.query.access_token to authenticate or share sessions + // or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312 + + ws.__session_id = location.query.session_id || Math.random(); + + ws.on('message', function (buffer) { + var cmd; + + try { + cmd = JSON.parse(buffer.toString('utf8')); + } catch(e) { + ws.send(JSON.stringify({ type: 'error', value: { message: e.message, code: "E_PARSE_JSON" } })); + } + + switch(cmd.type) { + case 'init': + break; + + case 'rpc': + break; + + default: + break; + } + + }); + + ws.send(JSON.stringify({ type: 'session', value: ws.__session_id })); + }); + + /* + var tablename = 'authn'; + if (tablename) { + setup.push(db.runAsync("CREATE TABLE IF NOT EXISTS '" + sanitize(tablename) + + "' (id TEXT, secret TEXT, json TEXT, PRIMARY KEY(id))")); + } + */ + + /*global Promise*/ + return new Promise(function (resolve) { + resolve(app); + }); +} + +module.exports.create = create; diff --git a/sqlite-wrapper.js b/sqlite-wrapper.js new file mode 100644 index 0000000..33bb334 --- /dev/null +++ b/sqlite-wrapper.js @@ -0,0 +1,61 @@ +'use strict'; + +/*global Promise*/ +var sqlite3 = require('sqlite3'); +var dbs = {}; + +function sanitize(str) { + return String(str).replace("'", "''"); +} + +function create(opts) { + var db; + + if (!opts) { + opts = {}; + } + + if (opts.verbose) { + sqlite3.verbose(); + } + + if (!dbs[opts.storage] || dbs[opts.storage].__key !== opts.key) { + dbs[opts.storage] = new sqlite3.Database(opts.storage); + } + + db = dbs[opts.storage]; + db.__key = opts.key; + + return new Promise(function (resolve, reject) { + db.serialize(function() { + var setup = []; + + if (opts.key) { + // TODO test key length + if (!opts.bits) { + opts.bits = 128; + } + + // TODO db.run(sql, function () { resolve() }); + setup.push(new Promise(function (resolve, reject) { + db.run("PRAGMA KEY = \"x'" + sanitize(opts.key) + "'\"", [], function (err) { + if (err) { reject(err); return; } + resolve(this); + }); + })); + setup.push(new Promise(function (resolve, reject) { + db.run("PRAGMA CIPHER = 'aes-" + sanitize(opts.bits) + "-cbc'", [], function (err) { + if (err) { reject(err); return; } + resolve(this); + }); + })); + } + + Promise.all(setup).then(function () { resolve(db); }, reject); + }); + }); +} + +module.exports.sanitize = sanitize; +module.exports.Database = sqlite3.Database; +module.exports.create = create;