npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

greenlock-manager-test

v3.1.1

Published

A simple test suite for Greenlock manager plugins.

Downloads

520

Readme

greenlock-manager-test.js

A simple test suite for Greenlock v3 manager plugins.

Greenlock Manager

A Greenlock Manager is responsible for tracking which domains belong on a certificate, when they are scheduled for renewal, and if they have been deleted.

It consists of two required functions:

set({ subject, altnames, renewAt, deletedAt });
get({ servername });

However, if you implement find({ subject, servernames, renewBefore }) (optional), you don't have to implement get().

The Right Way:

npm install --save greenlack
npx greenlock init --manager ./path-or-npm-name.js --manager-xxxx 'sets xxxx' --manager-yyyy 'set yyyy'

That creates a .greenlockrc, which is essentially the same as doing this:

var Greenlock = require("greenlock");
var greenlock = Greenlock.create({
    // ...

    manager: "./path-or-npm-name.js",
    xxxx: "sets xxxx",
    yyyy: "sets yyyy",
    packageRoot: __dirname
});

Why no require?

Okay, so you expect it to look like this:

var Greenlock = require("greenlock");
var greenlock = Greenlock.create({
    // WRONG!!
    manager: require("./path-or-npm-name.js").create({
        someOptionYouWant: true
    })
});

NOPE!

Greenlock is designed to so that the CLI tools, Web API, and JavaScript API can all work interdepedently, indpendently.

Therefore the configuration has to go into serializable JSON rather than executable JavaScript.

Quick Start

If you want to write a manager, the best way to start is by using one of the provided templates.

npm install --save-dev greenlock-manager-test
npx greenlock-manager-init

It will generate a bare bones manager that passes the tests, (skipping all optional features), and a test file:

"use strict";

var Manager = module.exports;
var db = {};

Manager.create = function(opts) {
    var manager = {};

    //
    // REQUIRED (basic issuance)
    //

    // Get
    manager.get = async function({ servername, wildname }) {
        // Required: find the certificate with the subject of `servername`
        // Optional (multi-domain certs support): find a certificate with `servername` as an altname
        // Optional (wildcard support): find a certificate with `wildname` as an altname

        // { subject, altnames, renewAt, deletedAt, challenges, ... }
        return db[servername] || db[wildname];
    };

    // Set
    manager.set = async function(opts) {
        // { subject, altnames, renewAt, deletedAt }
        // Required: updated `renewAt` and `deletedAt` for certificate matching `subject`

        var site = db[opts.subject] || {};
        db[opts.subject] = Object.assign(site, opts);
        return null;
    };

    //
    // Optional (Fully Automatic Renewal)
    //
    /*
    manager.find = async function(opts) {
        // { subject, servernames, altnames, renewBefore }

        return [{ subject, altnames, renewAt, deletedAt }];
    };
    //*/

    //
    // Optional (Special Remove Functionality)
    // The default behavior is to set `deletedAt`
    //
    /*
    manager.remove = async function(opts) {
    	return mfs.remove(opts);
    };
    //*/

    //
    // Optional (special settings save)
    // Implemented here because this module IS the fallback
    //
    /*
    manager.defaults = async function(opts) {
        if (opts) {
            return setDefaults(opts);
        }
        return getDefaults();
    };
    //*/

    //
    // Optional (for common deps and/or async initialization)
    //
    /*
    manager.init = async function(deps) {
        manager.request = deps.request;
        return null;
    };
    //*/

    return manager;
};
"use strict";

var Tester = require("greenlock-manager-test");

var Manager = require("./manager.js");
var config = {
    configFile: "greenlock-manager-test.delete-me.json"
};

Tester.test(Manager, config)
    .then(function(features) {
        console.info("PASS");
        console.info();
        console.info("Optional Feature Support:");
        features.forEach(function(feature) {
            console.info(
                feature.supported ? "✓ (YES)" : "✘ (NO) ",
                feature.description
            );
        });
        console.info();
    })
    .catch(function(err) {
        console.error("Oops, you broke it. Here are the details:");
        console.error(err.stack);
        console.error();
        console.error("That's all I know.");
    });
node manager.test.js
PASS:  get({ servername, wildname })
PASS:  set({ subject })

Optional Feature Support:
✘ (NO)  Multiple Domains per Certificate
✘ (NO)  Wildcard Certificates
✘ (NO)  Fully Automatic Renewal

Optional Features

If you're publishing a module to the community, you should implement the full test suite (and it's not that hard).

If you're only halfway through, you should note which features are supported and which aren't.

find({ subject, servernames, renewBefore });
defaults({ subscriberEmail, agreeToTerms, challenges, store, ... });
defaults(); // as getter
  • find() is used to get the full list of sites, for continuous fully automatic renewal.
  • defaults() exists so that the global config can be saved in the same place as the per-site config.
  • a proper get() should be able to search not just primary domains, but altnames as well.

Additionally, you're manager may need an init or a real delete - rather than just using set({ deletedAt }):

init({ request });
remove({ subject });

The Right Way™

If you want to publish a module to the community you should do a slightly better job:

module.exports.create = function(options) {
    var manager = {};

    // add some things to... wherever you save things

    manager.set = async function(siteConfig) {
        // You can see in the tests a sample of common values,
        // but you don't really need to worry about it.
        var subject = siteConfig.subject;

        // Cherry pick what you like for indexing / search, and JSONify the rest
        return mergeOrCreateSite(subject, siteConfig);
    };

    // find the things you've saved before

    manager.get = async function({ servername }) {
        return getSiteByAltname(servername);
    }
    manager.find = async function({ subject, servernames, renewBefore }) {
        var results = [];
        var gotten = {};

        if (subject) {
            var site = await getSiteBySubject(subject);
            if (site && site.subject === subject) {
                return [site];
            }
        }

        if (severnames) {
            return await Promise.all(servernames.map(function (altname) {
                var site = getSiteByAltname(subject);
                if (site && !gotten[site.subject]) {
                    gotten[site.subject] = true;
                    return site;
                }
            });
        }

        return getSitesThatShouldBeRenewedBefore(renewBefore || Infinity);
    };

    // delete a site config

    manager.remove = async function({ subject }) {
        // set deletedAt to a value, or actually delete it - however you like
        return mergeOrCreateSite(subject, { deletedAt: Date.now() });
    };

    // get / set global things

    manager.defaults = async function(options) {
        if (!options) {
            return getDefaultConfigValues();
        }

        return mergeDefaultConfigValues(options);
    };

    // optional, if you need it

    manager.init = async function(deps) {
        // a place to do some init, if you need it

        return doMyInit();

        // Also, `deps` will have some common dependencies
        // than many modules need, such as `request`.
        // This cuts down on stray dependencies, and helps
        // with browser compatibility.

        request = deps.request;
    };
};