WIP: reduce abstraction #6

Closed
coolaj86 wants to merge 7 commits from cleanup into kid-fix
4 changed files with 154 additions and 104 deletions
Showing only changes of commit b0c8905bf6 - Show all commits

172
README.md
View File

@ -2,70 +2,142 @@
> A database-driven Greenlock storage plugin with wildcard support.
## Features
* Many [Supported SQL Databases](http://docs.sequelizejs.com/manual/getting-started.html)
* [x] PostgreSQL (**best**)
* [x] SQLite3 (**easiest**)
* [x] Microsoft SQL Server (mssql)
* [x] MySQL, MariaDB
* Works on all platforms
* [x] Mac, Linux, VPS
* [x] AWS, Heroku, Akkeris, Docker
* [x] Windows
## Usage
To use, provide this Greenlock storage plugin as the `store` attribute when you
invoke `create`.
To use, provide this Greenlock storage plugin as the `store` option when you
invoke `create`:
```js
greenlock.create({
store: require('le-store-sequelize')
Greenlock.create({
store: require('greenlock-store-sequelize')
...
});
```
## Configuration
### Defaults
* SQLite3 (default)
* Database URLs / Connection Strings
* Environment variables
* Table Prefixes
No configuration is required. By default, you'll get a baked-in Sequelize
database running [sqlite3](https://www.npmjs.com/package/sqlite3).
### SQLite3 (default)
### Database Connection
SQLite3 is the default database, however, since it has a large number of dependencies
and may require a native module to be built, you must explicitly install
[sqlite3](https://www.npmjs.com/package/sqlite3):
Without `config.dbOptions`, the baked-in sequelize object uses sqlite3 with
defaults. If `config.dbOptions` is provided, you can configure the database
connection per the Sequelize documentation.
```bash
npm install --save sqlite3
```
If a dialect other than sqlite3 is used, dependencies will need to be
installed.
The default db file will be written wherever Greenlock's `configDir` is set to,
which is probably `~/acme` or `~/letsencrypt`.
```javascript
greenlock.create({
store: require('le-store-sequelize')({
dbConfig: {
username: 'mysqluser',
password: 'mysqlpassword',
database: 'mysqldatabase,
host: '127.0.0.1',
dialect: 'mysql'
```bash
~/acme/db.sqlite3
```
If you wish to set special options you may do so by passing a pre-configured `Sequelize` instance:
```js
var Sequelize = require('sequelize');
var db = new Sequelize({ dialect: 'sqlite', storage: '/Users/me/acme/db.sqlite3' });
Greenlock.create({
store: require('greenlock-store-sequelize').create({ db: db })
...
});
```
### Database URL Connection String
You may use database URLs (also known as 'connection strings') to initialize sequelize:
```js
var Sequelize = require('sequelize');
var db = new Sequelize('postgres://user:pass@hostname:port/database');
Greenlock.create({
store: require('greenlock-store-sequelize').create({ db: db })
...
});
```
If you need to use **custom options**, just instantiate sequelize directly:
```js
var dbUrl = 'postgres://user:pass@hostname:port/database';
Greenlock.create({
store: require('greenlock-store-sequelize').create({ storeDatabaseUrl: dbUrl })
...
});
```
For more information, see the [Sequelize Getting Started](http://docs.sequelizejs.com/manual/getting-started.html) docs.
### ENVs (i.e. for Docker, Heroku, Akkeris)
If your database connection string is in an environment variable,
you would use the usual standard for your platform.
For example, if you're using Heroku, Akkeris, or Docker you're
database connection string is probably `DATABASE_URL`, so you'd do something like this:
```js
var Sequelize = require('sequelize');
var databaseUrl = process.env['DATABASE_URL'];
var db = new Sequelize(databaseUrl);
Greenlock.create({
store: require('greenlock-store-sequelize').create({ db: db })
...
});
```
### Table Prefixes
The default table names are as follows:
* Keypair
* Domain
* Certificate
* Chain
If you'd like to add a table name prefix or define a specific schema within the database (PostgreSQL, SQL Server),
you can do so like this:
```js
var Sequelize = require('sequelize');
var databaseUrl = process.env['DATABASE_URL'];
var db = new Sequelize(databaseUrl, {
define: {
hooks: {
beforeDefine: function (name, attrs) {
console.log(name);
console.log(attrs);
attrs.tableName = 'MyPrefix' + attrs.tableName;
//attrs.schema = 'public';
}
}
}
})
});
```
The database can also be configured using an env variable.
```javascript
greenlock.create({
store: require('greenlock-store-sequelize')({
dbConfig: {
use_env_variable: 'DB_URL'
}
})
});
```
### Custom Database Object
If you already have a Sequelize object, you can pass that in as `config.db`,
circumventing the baked-in database entirely.
```javascript
var db = require('./db'); // your db
greenlock.create({
store: require('le-store-sequelize')({
db
})
});
Greenlock.create({
store: require('greenlock-store-sequelize').create({ db: db })
...
});
```

View File

@ -1,49 +1,25 @@
'use strict';
var fs = require('fs');
var path = require('path');
var basename = path.basename(__filename);
var Sequelize = require('sequelize');
var sync = require('../sync.js');
module.exports = function (config) {
module.exports = function (sequelize) {
var db = {};
db.Sequelize = Sequelize;
if (!config) {
config = {
dialect: "sqlite",
storage: "./db.sqlite"
};
}
if (config.use_env_variable) {
db.sequelize = new db.Sequelize(process.env[config.use_env_variable], config);
}
else {
db.sequelize = new db.Sequelize(config.database, config.username, config.password, config);
}
fs.readdirSync(__dirname).filter(function (file) {
return ('.' !== file[0]) && (file !== basename) && (file.slice(-3) === '.js');
}).forEach(function (file) {
var model = db.sequelize['import'](path.join(__dirname, file));
[ 'keypair.js'
, 'domain.js'
, 'certificate.js'
, 'chain.js'
].forEach(function (file) {
var model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db);
}
db[modelName].associate(db);
});
var synced = false;
if (!synced) {
return sync(db).then(function () {
synced = true;
return db;
});
}
return Promise.resolve(db);
return sync(db).then(function () {
return db;
});
};

View File

@ -6,21 +6,30 @@ module.exports.create = function (config={}) {
accounts: {},
certificates: {}
};
var Sequelize;
var sequelize = config.db;
var confDir = config.configDir || (require('os').homedir() + '/acme');
// The user can provide their own db, but if they don't, we'll use the
// baked-in db.
if (!config.db) {
if (!sequelize) {
// If the user provides options for the baked-in db, we'll use them. If
// they don't, we'll use the baked-in db with its defaults.
config.db = require('./db')(config.dbConfig || null);
}
else {
// This library expects config.db to resolve the db object. We'll ensure
// that this is the case with the provided db, as it was with the baked-in
// db.
config.db = Promise.resolve(config.db);
Sequelize = require('sequelize');
if (config.storeDatabaseUrl) {
sequelize = new Sequelize(config.storeDatabaseUrl);
} else {
sequelize = new Sequelize({ dialect: 'sqlite', storage: confDir + '/db.sqlite3' });
}
}
// This library expects config.db to resolve the db object. We'll ensure
// that this is the case with the provided db, as it was with the baked-in
// db.
config.db = Promise.resolve(sequelize).then(function (sequelize) {
return require('./db')(sequelize);
});
store.certificates.check = function (opts) {
return config.db.then(function (db) {
return db.Certificate.findOne({
@ -49,7 +58,7 @@ module.exports.create = function (config={}) {
err.code = 'ENOENT';
throw err;
}).catch(function (err) {
if (err.code == 'ENOENT') {
if (err.code === 'ENOENT') {
return null;
}
throw err;
@ -77,7 +86,7 @@ module.exports.create = function (config={}) {
err.code = 'ENOENT';
throw err;
}).catch(function (err) {
if (err.code == 'ENOENT') {
if (err.code === 'ENOENT') {
return null;
}
throw err;
@ -119,7 +128,7 @@ module.exports.create = function (config={}) {
err.code = 'ENOENT';
throw err;
}).catch(function (err) {
if (err.code == 'ENOENT') {
if (err.code === 'ENOENT') {
return null;
}
throw err;

View File

@ -6,17 +6,10 @@ function sync(db) {
function next() {
var modelName = keys.shift();
if (!modelName) { return; }
if (isModel(modelName)) {
return db[modelName].sync().then(next);
}
return next();
return db[modelName].sync().then(next);
}
return Promise.resolve().then(next);
}
function isModel(key) {
return !(['sequelize','Sequelize'].includes(key));
}
module.exports = sync;