cleanup: rework constructor and README #11
							
								
								
									
										221
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										221
									
								
								README.md
									
									
									
									
									
								
							@ -2,70 +2,197 @@
 | 
			
		||||
 | 
			
		||||
> 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
 | 
			
		||||
<details><summary>SQLite3 (default)</summary>
 | 
			
		||||
 | 
			
		||||
No configuration is required. By default, you'll get a baked-in Sequelize
 | 
			
		||||
database running [sqlite3](https://www.npmjs.com/package/sqlite3).
 | 
			
		||||
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):
 | 
			
		||||
 | 
			
		||||
### Database Connection
 | 
			
		||||
```bash
 | 
			
		||||
npm install --save 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.
 | 
			
		||||
The default db file will be written wherever Greenlock's `configDir` is set to,
 | 
			
		||||
which is probably `~/acme` or `~/letsencrypt`.
 | 
			
		||||
 | 
			
		||||
If a dialect other than sqlite3 is used, dependencies will need to be
 | 
			
		||||
installed.
 | 
			
		||||
```bash
 | 
			
		||||
~/acme/db.sqlite3
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
greenlock.create({
 | 
			
		||||
  store: require('le-store-sequelize')({
 | 
			
		||||
    dbConfig: {
 | 
			
		||||
      username: 'mysqluser',
 | 
			
		||||
      password: 'mysqlpassword',
 | 
			
		||||
      database: 'mysqldatabase,
 | 
			
		||||
      host: '127.0.0.1',
 | 
			
		||||
      dialect: 'mysql'
 | 
			
		||||
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 })
 | 
			
		||||
  ...
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
<details><summary>PostgreSQL, SQL Server, and lesser databases...</summary>
 | 
			
		||||
 | 
			
		||||
The general format of a DATABASE_URL is something like this:
 | 
			
		||||
 | 
			
		||||
> `schema://user:pass@server:port/service?option=foo`
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
 | 
			
		||||
> `postgres://aj:secret123@127.0.0.1:5432/greenlock`
 | 
			
		||||
 | 
			
		||||
For each database the exact format may be slightly different:
 | 
			
		||||
 | 
			
		||||
* `postgres://user:pass@hostname:port/database?option=foo`
 | 
			
		||||
* `sqlserver://user:pass@datasource:port/instance/catalog?database=dbname` (mssql)
 | 
			
		||||
* `mysql://user:pass@hostname:port/database?option=foo`
 | 
			
		||||
* `mariadb://user:pass@hostname:port/database?option=foo`
 | 
			
		||||
 | 
			
		||||
There's also a way to specify objects instead of using the standard connection strings.
 | 
			
		||||
 | 
			
		||||
See the next section for more information.
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
<details><summary>Database URLs / Connection Strings</summary>
 | 
			
		||||
You may use database URLs (also known as 'connection strings') to initialize sequelize:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
var dbUrl = 'postgres://user:pass@hostname:port/database';
 | 
			
		||||
 | 
			
		||||
Greenlock.create({
 | 
			
		||||
  store: require('greenlock-store-sequelize').create({ storeDatabaseUrl: dbUrl })
 | 
			
		||||
  ...
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If you need to use **custom options**, just instantiate sequelize directly:
 | 
			
		||||
 | 
			
		||||
```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 })
 | 
			
		||||
  ...
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
See the [Sequelize Getting Started](http://docs.sequelizejs.com/manual/getting-started.html) docs for more info
 | 
			
		||||
on database options for sequelize.
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
<details><summary>Environment variables (AWS, Docker, Heroku, Akkeris)</summary>
 | 
			
		||||
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 })
 | 
			
		||||
  ...
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
<details><summary>Table Prefixes</summary>
 | 
			
		||||
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, {
 | 
			
		||||
    hooks: {
 | 
			
		||||
        beforeDefine: function (columns, model) {
 | 
			
		||||
          model.tableName = 'MyPrefix' + model.name.plural;
 | 
			
		||||
          //model.schema = 'public';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Greenlock.create({
 | 
			
		||||
  store: require('greenlock-store-sequelize').create({ db: db })
 | 
			
		||||
  ...
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
The database can also be configured using an env variable.
 | 
			
		||||
## Table Structure
 | 
			
		||||
 | 
			
		||||
```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
 | 
			
		||||
  })
 | 
			
		||||
});
 | 
			
		||||
This is the table structure that's created.
 | 
			
		||||
 | 
			
		||||
```sql
 | 
			
		||||
CREATE TABLE `Keypairs` (
 | 
			
		||||
  `id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
			
		||||
  `xid` VARCHAR(255) UNIQUE,
 | 
			
		||||
  `content` TEXT,
 | 
			
		||||
  `createdAt` DATETIME NOT NULL,
 | 
			
		||||
  `updatedAt` DATETIME NOT NULL);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE `Domains` (
 | 
			
		||||
  `id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
			
		||||
  `subject` VARCHAR(255) UNIQUE,
 | 
			
		||||
  `altnames` TEXT,
 | 
			
		||||
  `createdAt` DATETIME NOT NULL,
 | 
			
		||||
  `updatedAt` DATETIME NOT NULL);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE `Certificates` (
 | 
			
		||||
  `id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
			
		||||
  `subject` VARCHAR(255) UNIQUE,
 | 
			
		||||
  `cert` TEXT,
 | 
			
		||||
  `issuedAt` DATETIME,
 | 
			
		||||
  `expiresAt` DATETIME,
 | 
			
		||||
  `altnames` TEXT,
 | 
			
		||||
  `chain` TEXT,
 | 
			
		||||
  `createdAt` DATETIME NOT NULL,
 | 
			
		||||
  `updatedAt` DATETIME NOT NULL);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE `Chains` (
 | 
			
		||||
  `id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
			
		||||
  `xid` VARCHAR(255) UNIQUE,
 | 
			
		||||
  `content` TEXT,
 | 
			
		||||
  `createdAt` DATETIME NOT NULL,
 | 
			
		||||
  `updatedAt` DATETIME NOT NULL,
 | 
			
		||||
  `CertificateId` INTEGER REFERENCES
 | 
			
		||||
  `Certificates` (`id`) ON DELETE SET NULL ON UPDATE CASCADE);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								db/index.js
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								db/index.js
									
									
									
									
									
								
							@ -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;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								sync.js
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								sync.js
									
									
									
									
									
								
							@ -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;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user