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…
Reference in New Issue