Compare commits

...

40 Commits

Author SHA1 Message Date
b4ebdbae47 2.0.19 2019-01-19 02:44:52 +01:00
c55d2dcf10 fix(core): update 2019-01-19 02:44:52 +01:00
7274d5ea8a 2.0.18 2019-01-19 02:25:35 +01:00
1ec733c2a9 fix(core): update 2019-01-19 02:25:34 +01:00
3ef14d8ac7 2.0.17 2019-01-18 01:33:02 +01:00
f332bf95fe fix(core): update 2019-01-18 01:33:01 +01:00
6acad8a306 2.0.16 2019-01-18 00:46:09 +01:00
6ae672f707 fix(core): update 2019-01-18 00:46:08 +01:00
3d263e2181 2.0.15 2019-01-18 00:45:29 +01:00
85639f29af fix(core): update 2019-01-18 00:45:29 +01:00
85448a21fc 2.0.14 2019-01-17 13:39:16 +01:00
3b85c4a37e fix(core): update 2019-01-17 13:39:15 +01:00
67dd650dce 2.0.13 2019-01-17 02:50:32 +01:00
92eaf5f19a fix(core): update 2019-01-17 02:50:32 +01:00
fcf9a61b1d 2.0.12 2019-01-09 14:13:32 +01:00
fbd258a876 fix(core): update 2019-01-09 14:13:32 +01:00
c65b8fc1af 2.0.11 2019-01-09 14:10:57 +01:00
b7b588d713 fix(core): update 2019-01-09 14:10:57 +01:00
00d672c135 2.0.10 2019-01-09 12:18:56 +01:00
6a1e778b49 fix(core): update 2019-01-09 12:18:56 +01:00
4cfb26f62f 2.0.9 2019-01-09 12:18:07 +01:00
7ba3ad0b21 fix(core): update 2019-01-09 12:18:06 +01:00
cd93ec2560 2.0.8 2019-01-09 12:15:29 +01:00
ede884930e fix(core): update 2019-01-09 12:15:28 +01:00
1049920efd 2.0.7 2019-01-01 22:35:18 +01:00
b7a34403c5 fix(core): update 2019-01-01 22:35:18 +01:00
987e19372a 2.0.6 2019-01-01 22:34:37 +01:00
0b97aaee91 fix(core): update 2019-01-01 22:34:36 +01:00
b87ca1fa03 2.0.5 2018-08-11 15:10:28 +02:00
824f872fa5 fix(tests): update 2018-08-11 15:10:27 +02:00
80248c77d0 2.0.4 2018-08-11 15:09:19 +02:00
f592150646 fix(host handling): update 2018-08-11 15:09:19 +02:00
f24652936a 2.0.3 2018-08-11 00:29:16 +02:00
79af6c4a68 fix(CI): accessLevel 2018-08-11 00:29:16 +02:00
fafc757930 2.0.2 2018-08-11 00:25:20 +02:00
ead5e1a7bd fix(CI): now installing stufall alright 2018-08-11 00:25:20 +02:00
ad57be180a 2.0.1 2018-08-10 23:44:31 +02:00
7cc4ce02c9 fix(core): update 2018-08-10 23:44:31 +02:00
70dace595b 2.0.0 2018-08-10 23:10:48 +02:00
9b043d0e0d BREAKING CHANGE(scope): change scope to @pushrocks 2018-08-10 23:10:48 +02:00
20 changed files with 1993 additions and 360 deletions

View File

@ -1,4 +1,4 @@
image: hosttoday/ht-docker-node:npmts image: hosttoday/ht-docker-node:npmci
stages: stages:
- test - test
@ -10,17 +10,20 @@ before_script:
testSTABLE: testSTABLE:
stage: test stage: test
script: script:
- npmci test stable - npmci npm install
- npmci npm test stable
only: only:
- tags - tags
tags: tags:
- docker - docker
- notpriv
release: release:
stage: release stage: release
environment: npmjs-com_registry environment: npmjs-com_registry
script: script:
- npmci publish - npmci npm prepare
- npmci npm publish
only: only:
- tags - tags
tags: tags:

View File

@ -1,26 +1,29 @@
# smartnginx # smartnginx
control nginx from node, TypeScript ready control nginx from node, TypeScript ready
## Status ## Status
[![build status](https://gitlab.com/pushrocks/smartnginx/badges/master/build.svg)](https://gitlab.com/pushrocks/smartnginx/commits/master) [![build status](https://gitlab.com/pushrocks/smartnginx/badges/master/build.svg)](https://gitlab.com/pushrocks/smartnginx/commits/master)
## Features ## Features
* easy reverse configuration - easy reverse configuration
* automatic letsencrypt DNS01 challenge based ssl cert generation - automatic letsencrypt DNS01 challenge based ssl cert generation
* automatic nginx process handling zero-downtime config reloading - automatic nginx process handling zero-downtime config reloading
* works in Docker environements - works in Docker environements
## Usage ## Usage
We recommend the use of TypeScript! :) We recommend the use of TypeScript! :)
```typescript ```typescript
import * as smartnginx from "smartnginx"; import * as smartnginx from 'smartnginx';
myNginxConfig = new smartnginx.NginxConfig(); const smartnginxInstance = new smartnginx.SmartNginx();
myNginxZone = new smartnginx.NginxZone({ myNginxHost = new smartnginx.NginxHost({
zoneName:"some.example.com", hostName: 'some.example.com',
type:"reverseProxy", type: 'reverseProxy',
destination:"192.192.192.192" // some destination IP destination: '192.192.192.192' // some destination IP
}); });
myNginxConfig.addZone(myNginxZone); // adds the zone to the config myNginxConfig.addZone(myNginxZone); // adds the zone to the config
myNginxConfig.deploy(); // deploys the referenced NginxConfig and gracefully reloads it myNginxConfig.deploy(); // deploys the referenced NginxConfig and gracefully reloads it

8
npmextra.json Normal file
View File

@ -0,0 +1,8 @@
{
"npmci": {
"npmAccessLevel": "public"
},
"npmdocker": {
}
}

1603
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,14 @@
{ {
"name": "smartnginx", "name": "@pushrocks/smartnginx",
"version": "1.0.6", "version": "2.0.19",
"private": false,
"description": "control nginx from node, TypeScript ready", "description": "control nginx from node, TypeScript ready",
"main": "dist/index.js", "main": "dist/index.js",
"typings": "dist/index.d.ts", "typings": "dist/index.d.ts",
"scripts": { "scripts": {
"test": "(npmts)", "test": "tstest test/",
"cleanTest": "(rm -r nginxconfig)" "cleanTest": "(rm -r nginxconfig) && npm run test",
"build": "(tsbuild)"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -22,19 +24,20 @@
}, },
"homepage": "https://gitlab.com/pushrocks/smartnginx#README", "homepage": "https://gitlab.com/pushrocks/smartnginx#README",
"dependencies": { "dependencies": {
"@types/q": "0.x.x", "@pushrocks/lik": "^3.0.4",
"@types/shelljs": "0.x.x", "@pushrocks/smartfile": "^6.0.11",
"beautylog": "^5.0.20", "@pushrocks/smartlog": "^2.0.11",
"cert": "^1.0.7", "@pushrocks/smartpromise": "^2.0.5",
"q": "^1.4.1", "@pushrocks/smartshell": "^2.0.13",
"shelljs": "^0.7.3", "@pushrocks/smartstring": "^3.0.8"
"smartfile": "^4.0.13",
"smartstring": "^2.0.15"
}, },
"devDependencies": { "devDependencies": {
"npmts-g": "^5.2.8", "@gitzone/tsbuild": "^2.1.4",
"qenv": "^1.0.8", "@gitzone/tsrun": "^1.1.17",
"should": "^10.0.0", "@gitzone/tstest": "^1.0.18",
"typings-test": "^1.0.1" "@pushrocks/qenv": "^4.0.0",
"@pushrocks/tapbundle": "^3.0.7",
"tslint": "^5.12.1",
"tslint-config-prettier": "^1.17.0"
} }
} }

View File

@ -1,3 +1 @@
vars: vars:
- CF_EMAIL
- CF_KEY

2
test/test.d.ts vendored
View File

@ -1,2 +0,0 @@
import "typings-test";
import "should";

View File

@ -1,61 +0,0 @@
"use strict";
require("typings-test");
const path = require("path");
require("should");
const qenv_1 = require("qenv");
const smartnginx = require("../dist/index");
// setup environment
let testQenv = new qenv_1.Qenv(process.cwd(), path.join(process.cwd(), ".nogit"));
describe("smartnginx", function () {
let testNginxConfig;
let testNginxZone01;
let testNginxZone02;
describe("NginxZone", function () {
it(`"new NginxZone()" should produce an instance of NginxConfig`, function () {
testNginxZone01 = new smartnginx.NginxHost({
hostName: "test100.bleu.de",
type: smartnginx.hostTypes.reverseProxy,
destination: "192.192.192.191"
});
testNginxZone02 = new smartnginx.NginxHost({
hostName: "test102.bleu.de",
type: smartnginx.hostTypes.reverseProxy,
destination: "192.192.192.192"
});
testNginxZone01.should.be.instanceof(smartnginx.NginxHost);
console.log(testNginxZone01.configString);
});
});
describe("NginxConfig", function () {
this.timeout(10000);
it(`"new NginxConfig()" should produce an instance of NginxConfig`, function () {
testNginxConfig = new smartnginx.NginxConfig({
cfEmail: process.env.CF_EMAIL,
cfKey: process.env.CF_KEY,
testMode: true
});
testNginxConfig.should.be.instanceof(smartnginx.NginxConfig);
});
describe(".addZone()", function () {
it("should add a zone to NginxConfig Object", function () {
testNginxConfig.addHost(testNginxZone01);
testNginxConfig.addHost(testNginxZone02);
});
});
describe(".deploy()", function () {
this.timeout(600000);
it("should deploy a config from an instance", function (done) {
testNginxConfig.deploy()
.then(() => {
done();
});
});
});
describe(".stop", function () {
it("should end the process", function () {
testNginxConfig.nginxProcess.stop();
});
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLFFBQU8sY0FBYyxDQUFDLENBQUE7QUFDdEIsTUFBTyxJQUFJLFdBQVcsTUFBTSxDQUFDLENBQUM7QUFDOUIsUUFBTyxRQUFRLENBQUMsQ0FBQTtBQUNoQix1QkFBbUIsTUFBTSxDQUFDLENBQUE7QUFDMUIsTUFBWSxVQUFVLFdBQU0sZUFBZSxDQUFDLENBQUE7QUFFNUMsb0JBQW9CO0FBQ3BCLElBQUksUUFBUSxHQUFHLElBQUksV0FBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO0FBQ3pFLFFBQVEsQ0FBQyxZQUFZLEVBQUM7SUFDbEIsSUFBSSxlQUFzQyxDQUFDO0lBQzNDLElBQUksZUFBb0MsQ0FBQztJQUN6QyxJQUFJLGVBQW9DLENBQUM7SUFDekMsUUFBUSxDQUFDLFdBQVcsRUFBQztRQUNqQixFQUFFLENBQUMsNkRBQTZELEVBQUM7WUFDN0QsZUFBZSxHQUFHLElBQUksVUFBVSxDQUFDLFNBQVMsQ0FBQztnQkFDdkMsUUFBUSxFQUFDLGlCQUFpQjtnQkFDMUIsSUFBSSxFQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsWUFBWTtnQkFDdEMsV0FBVyxFQUFDLGlCQUFpQjthQUNoQyxDQUFDLENBQUM7WUFDSCxlQUFlLEdBQUcsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDO2dCQUN2QyxRQUFRLEVBQUMsaUJBQWlCO2dCQUMxQixJQUFJLEVBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxZQUFZO2dCQUN0QyxXQUFXLEVBQUMsaUJBQWlCO2FBQ2hDLENBQUMsQ0FBQztZQUNILGVBQWUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDM0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDOUMsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDLENBQUMsQ0FBQztJQUNILFFBQVEsQ0FBQyxhQUFhLEVBQUM7UUFDbkIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNwQixFQUFFLENBQUMsK0RBQStELEVBQUM7WUFDL0QsZUFBZSxHQUFHLElBQUksVUFBVSxDQUFDLFdBQVcsQ0FBQztnQkFDekMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUTtnQkFDN0IsS0FBSyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTTtnQkFDekIsUUFBUSxFQUFDLElBQUk7YUFDaEIsQ0FBQyxDQUFDO1lBQ0gsZUFBZSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNqRSxDQUFDLENBQUMsQ0FBQztRQUNILFFBQVEsQ0FBQyxZQUFZLEVBQUM7WUFDbEIsRUFBRSxDQUFDLHlDQUF5QyxFQUFDO2dCQUN6QyxlQUFlLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO2dCQUN6QyxlQUFlLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQzdDLENBQUMsQ0FBQyxDQUFBO1FBQ04sQ0FBQyxDQUFDLENBQUM7UUFDSCxRQUFRLENBQUMsV0FBVyxFQUFDO1lBQ2pCLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDckIsRUFBRSxDQUFDLHlDQUF5QyxFQUFDLFVBQVMsSUFBSTtnQkFDdEQsZUFBZSxDQUFDLE1BQU0sRUFBRTtxQkFDbkIsSUFBSSxDQUFDO29CQUNGLElBQUksRUFBRSxDQUFDO2dCQUNYLENBQUMsQ0FBQyxDQUFDO1lBQ1gsQ0FBQyxDQUFDLENBQUE7UUFDTixDQUFDLENBQUMsQ0FBQztRQUNILFFBQVEsQ0FBQyxPQUFPLEVBQUM7WUFDYixFQUFFLENBQUMsd0JBQXdCLEVBQUM7Z0JBQ3hCLGVBQWUsQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDeEMsQ0FBQyxDQUFDLENBQUE7UUFDTixDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQyxDQUFDLENBQUMifQ==

View File

@ -1,60 +1,53 @@
import "typings-test"; import { tap, expect } from '@pushrocks/tapbundle';
import path = require("path"); import path = require('path');
import "should";
import {Qenv} from "qenv";
import * as smartnginx from "../dist/index";
// setup environment import { Qenv } from '@pushrocks/qenv';
let testQenv = new Qenv(process.cwd(),path.join(process.cwd(),".nogit")); const testQenv = new Qenv('./', './.nogit/');
describe("smartnginx",function(){
let testNginxConfig:smartnginx.NginxConfig; import * as smartnginx from '../ts/index';
let testNginxZone01:smartnginx.NginxHost;
let testNginxZone02:smartnginx.NginxHost; let testSmartNginx: smartnginx.SmartNginx;
describe("NginxZone",function(){ let testNginxZone01: smartnginx.NginxHost;
it(`"new NginxZone()" should produce an instance of NginxConfig`,function(){ let testNginxZone02: smartnginx.NginxHost;
testNginxZone01 = new smartnginx.NginxHost({
hostName:"test100.bleu.de", tap.test('should create a valid instance of SmartNginx', async () => {
type:smartnginx.hostTypes.reverseProxy, testSmartNginx = new smartnginx.SmartNginx({});
destination:"192.192.192.191" expect(testSmartNginx).to.be.instanceof(smartnginx.SmartNginx);
});
testNginxZone02 = new smartnginx.NginxHost({
hostName:"test102.bleu.de",
type:smartnginx.hostTypes.reverseProxy,
destination:"192.192.192.192"
});
testNginxZone01.should.be.instanceof(smartnginx.NginxHost);
console.log(testNginxZone01.configString);
});
});
describe("NginxConfig",function(){
this.timeout(10000);
it(`"new NginxConfig()" should produce an instance of NginxConfig`,function(){
testNginxConfig = new smartnginx.NginxConfig({
cfEmail: process.env.CF_EMAIL,
cfKey: process.env.CF_KEY,
testMode:true
});
testNginxConfig.should.be.instanceof(smartnginx.NginxConfig);
});
describe(".addZone()",function(){
it("should add a zone to NginxConfig Object",function(){
testNginxConfig.addHost(testNginxZone01);
testNginxConfig.addHost(testNginxZone02);
})
});
describe(".deploy()",function(){
this.timeout(600000);
it("should deploy a config from an instance",function(done){
testNginxConfig.deploy()
.then(() => {
done();
});
})
});
describe(".stop",function(){
it("should end the process",function(){
testNginxConfig.nginxProcess.stop();
})
})
});
}); });
tap.test(`should produce an instance of NginxConfig`, async () => {
testNginxZone01 = new smartnginx.NginxHost(testSmartNginx, {
hostName: 'test100.bleu.de',
destination: '192.192.192.191',
privateKey: 'some private',
publicKey: 'some public'
});
testNginxZone02 = new smartnginx.NginxHost(testSmartNginx, {
hostName: 'test102.bleu.de',
destination: '192.192.192.192',
privateKey: 'some private',
publicKey: 'some public'
});
expect(testNginxZone01).to.be.instanceof(smartnginx.NginxHost);
});
tap.test('.addHostCandidate() should add a zone to NginxConfig Object', async () => {
testSmartNginx.addHostCandidate(testNginxZone01);
testSmartNginx.addHostCandidate(testNginxZone02);
});
tap.test('.deploy() should deploy a config from an instance', async () => {
await testSmartNginx.deploy();
});
tap.test('should not redeploy', async () => {
testSmartNginx.addHostCandidate(testNginxZone01);
testSmartNginx.addHostCandidate(testNginxZone02);
await testSmartNginx.deploy();
});
tap.test('.stop() should end the process', async () => {
testSmartNginx.nginxProcess.stop();
});
tap.start();

View File

@ -1,6 +1,6 @@
import * as plugins from "./smartnginx.plugins"; import * as plugins from './smartnginx.plugins';
// classes // classes
export * from "./smartnginx.classes.nginxconfig"; export * from './smartnginx.classes.smartnginx';
export * from "./smartnginx.classes.nginxprocess"; export * from './smartnginx.classes.nginxprocess';
export * from "./smartnginx.classes.nginxhost"; export * from './smartnginx.classes.nginxhost';

View File

@ -0,0 +1,6 @@
export interface IHostConfig {
hostName: string;
destination: string;
privateKey: string;
publicKey: string;
}

0
ts/interfaces/index.ts Normal file
View File

View File

@ -1,71 +0,0 @@
import * as plugins from "./smartnginx.plugins";
import * as paths from "./smartnginx.paths";
import * as snippets from "./smartnginx.snippets";
import { NginxHost } from "./smartnginx.classes.nginxhost";
import { NginxProcess } from "./smartnginx.classes.nginxprocess";
let allConfigs: NginxConfig[] = [];
/**
* main class that manages a NginxInstance
*/
export class NginxConfig {
hosts: NginxHost[] = [];
cert: plugins.cert.Cert; // the Cert Instance from which the config gets its certificates
nginxProcess: NginxProcess = new NginxProcess(this);
isDeployed: boolean = false;
constructor(optionsArg: plugins.cert.ICertConstructorOptions) {
this.cert = new plugins.cert.Cert({
cfEmail: optionsArg.cfEmail,
cfKey: optionsArg.cfKey,
sslDir: paths.nginxCertBase,
gitOriginRepo: optionsArg.gitOriginRepo,
testMode: optionsArg.testMode
});
};
// interact with Hosts
addHost(nginxHostArg: NginxHost){
this.hosts.push(nginxHostArg);
}
listHosts(): NginxHost[]{
return this.hosts;
};
removeHost(nginxHostArg: NginxHost) {
}
clean() {
this.hosts = [];
}
// handle deployment of hosts
deploy() {
let done = plugins.q.defer();
plugins.smartfile.fs.ensureDirSync(paths.nginxConfigBase);
plugins.smartfile.fs.ensureDirSync(paths.nginxHostFileBase);
plugins.smartfile.fs.ensureDirSync(paths.nginxCertBase);
for (let config of allConfigs) {
config.isDeployed = false;
};
this.isDeployed = true;
// write base config
plugins.smartfile.memory.toFsSync(
snippets.getBaseConfigString(),
paths.nginxConfFile
);
// deploy hosts
let promiseArray = [];
for (let host of this.hosts) {
let hostDeployedPromise = host.deploy(this.cert);
hostDeployedPromise.then(() => {
plugins.beautylog.info(`Host ${host.hostName} deployed!`);
this.nginxProcess.reloadConfig();
});
promiseArray.push(hostDeployedPromise);
};
plugins.q.all(promiseArray)
.then(() => {
done.resolve();
});
return done.promise;
};
};

View File

@ -1,43 +1,53 @@
import * as plugins from "./smartnginx.plugins"; import * as plugins from './smartnginx.plugins';
import * as paths from "./smartnginx.paths"; import * as paths from './smartnginx.paths';
import * as snippets from "./smartnginx.snippets" import * as snippets from './smartnginx.snippets';
/** import { SmartNginx } from './smartnginx.classes.smartnginx';
* the host config data that NginxHost needs to create a valid instance
*/ import { IHostConfig } from './interfaces/hostconfig';
export interface IHostConfigData {
hostName: string,
type: hostTypes,
destination: string
}
export enum hostTypes { export enum hostTypes {
reverseProxy, reverseProxy
static
} }
/** /**
* manages a single nginx host * manages a single nginx host
*/ */
export class NginxHost { export class NginxHost implements IHostConfig {
hostName: string; // the host name e.g. domain name /**
type: hostTypes; * smartnginxInstance this NginHost belongs to
destination: string; */
configString: string; // the actual host config file as string public smartnginxInstance: SmartNginx;
constructor(optionsArg:IHostConfigData) {
public hostName: string; // the host name e.g. domain name
public destination: string;
public configString: string; // the actual host config file as string
public privateKey: string;
public publicKey: string;
constructor(smartnginxInstanceArg: SmartNginx, optionsArg: IHostConfig) {
this.smartnginxInstance = smartnginxInstanceArg;
this.hostName = optionsArg.hostName; this.hostName = optionsArg.hostName;
this.type = optionsArg.type;
this.destination = optionsArg.destination; this.destination = optionsArg.destination;
this.configString = snippets.getHostConfigString(optionsArg.hostName, optionsArg.destination); this.configString = snippets.getHostConfigString(optionsArg.hostName, optionsArg.destination);
}; this.privateKey = optionsArg.privateKey;
deploy(certInstanceArg: plugins.cert.Cert) { this.publicKey = optionsArg.publicKey;
let done = plugins.q.defer(); }
let filePath = plugins.path.join(paths.nginxHostFileBase, this.hostName + ".conf");
/**
*
* @param certInstanceArg
*/
public async deploy() {
const filePathConfig = plugins.path.join(paths.nginxHostDirPath, `${this.hostName}.conf`);
const filePathPrivate = plugins.path.join(paths.nginxHostDirPath, `${this.hostName}.private.pem`);
const filePathPublic = plugins.path.join(paths.nginxHostDirPath, `${this.hostName}.public.pem`);
// writeConfig // writeConfig
plugins.smartfile.memory.toFsSync(this.configString, filePath); plugins.smartfile.memory.toFsSync(this.configString, filePathConfig);
// get cert
certInstanceArg.getDomainCert(this.hostName) // write ssl
.then(done.resolve); plugins.smartfile.memory.toFsSync(this.privateKey, filePathPrivate);
return done.promise; plugins.smartfile.memory.toFsSync(this.publicKey, filePathPublic);
};
}; }
}

View File

@ -1,74 +1,69 @@
import * as plugins from "./smartnginx.plugins"; import * as plugins from './smartnginx.plugins';
import * as paths from "./smartnginx.paths"; import * as paths from './smartnginx.paths';
import { NginxConfig } from "./smartnginx.classes.nginxconfig"; import { SmartNginx } from './smartnginx.classes.smartnginx';
import { NginxHost } from "./smartnginx.classes.nginxhost"; import { NginxHost } from './smartnginx.classes.nginxhost';
import { Smartshell } from '@pushrocks/smartshell';
import { ChildProcess } from 'child_process';
/** /**
* manages a nginxprocess for an NginxConfig * manages a nginxprocess for an NginxConfig
*/ */
export class NginxProcess { export class NginxProcess {
started: boolean = false; public started: boolean = false;
nginxConfig:NginxConfig; public smartNginxRef: SmartNginx;
nginxChildProcess: plugins.childProcess.ChildProcess; private nginxChildProcess: ChildProcess;
constructor(nginxConfigArg) { private smartshellInstance = new Smartshell({
this.nginxConfig = nginxConfigArg; executor: 'bash'
}; });
constructor(nginxRefArg: SmartNginx) {
this.smartNginxRef = nginxRefArg;
}
/** /**
* start nginx * start nginx
*/ */
start() { public async start() {
let done = plugins.q.defer(); if (!this.nginxChildProcess) {
if (typeof this.nginxChildProcess == "undefined"){ this.nginxChildProcess = (await this.smartshellInstance.execStreaming(
this.nginxChildProcess = plugins.childProcess.exec(`nginx -c ${paths.nginxConfFile}`, function (error, stdout, stderr) { `nginx -c ${paths.nginxConfFile}`
console.log(`stdout: ${stdout}`); )).childProcess;
console.log(`stderr: ${stderr}`); }
if (error !== null) {
console.log(`exec error: ${error}`);
};
});
};
this.started = true; this.started = true;
plugins.beautylog.info("started Nginx!"); plugins.smartlog.defaultLogger.log('info', 'started Nginx!');
done.resolve(); }
return done.promise;
};
/** /**
* reload config * reload config
*/ */
reloadConfig(){ public async reloadConfig() {
let done = plugins.q.defer(); if (!this.started) {
if(this.started == false){
this.start(); this.start();
} else { } else {
plugins.shelljs.exec("nginx -s reload"); await this.smartshellInstance.exec('nginx -s reload');
}; }
plugins.beautylog.ok("NginxProcess has loaded the new config!") this.smartNginxRef.logger.log('info', 'NginxProcess has loaded the new config!');
done.resolve(); }
return done.promise;
};
/** /**
* stop the nginx instance * stop the nginx instance
*/ */
stop() { public async stop() {
let done = plugins.q.defer(); if (this.nginxChildProcess) {
if (typeof this.nginxChildProcess != "undefined") { this.smartshellInstance.exec('nginx -s quit');
plugins.shelljs.exec("nginx -s quit");
this.started = false; this.started = false;
plugins.beautylog.info("stopped Nginx!"); this.smartNginxRef.logger.log('info', 'stopped Nginx!');
} else { } else {
plugins.beautylog.log("nginx already stopped!"); this.smartNginxRef.logger.log('info', 'nginx already stopped!');
}; }
done.resolve(); }
return done.promise;
};
/** /**
* checks if nginx is in path * checks if nginx is in path
*/ */
check(): boolean { public check(): boolean {
return; return;
}; }
} }

View File

@ -0,0 +1,124 @@
import * as plugins from './smartnginx.plugins';
import * as paths from './smartnginx.paths';
import * as snippets from './smartnginx.snippets';
import { NginxHost } from './smartnginx.classes.nginxhost';
import { NginxProcess } from './smartnginx.classes.nginxprocess';
import { IHostConfig } from './interfaces/hostconfig';
/**
* main class that manages a NginxInstance
*/
export class SmartNginx {
public logger: plugins.smartlog.Smartlog;
// the objectmaps
private deployedHosts = new plugins.lik.Objectmap<NginxHost>();
private hostCandidates = new plugins.lik.Objectmap<NginxHost>();
public nginxProcess: NginxProcess = new NginxProcess(this);
constructor(optionsArg: { logger?: plugins.smartlog.Smartlog }) {
optionsArg.logger
? (this.logger = optionsArg.logger)
: (this.logger = plugins.smartlog.defaultLogger);
}
// ===================
// interact with Hosts
// ===================
/**
* add a host
* @param nginxHostArg
*/
public addHostCandidate(optionsArg: IHostConfig): NginxHost {
const nginxHost = new NginxHost(this, optionsArg);
this.hostCandidates.add(nginxHost);
return nginxHost;
}
/**
* Gets a NginxHost by hostname
* @param hostNameArg
*/
public getDeployedNginxHostByHostName(hostNameArg: string): NginxHost {
return this.deployedHosts.find(nginxHost => {
return nginxHost.hostName === hostNameArg;
});
}
/**
* listHosts
*/
public async listDeployedHosts(): Promise<NginxHost[]> {
return this.deployedHosts.getArray();
}
/**
* remove a Host
* @param nginxHostArg
*/
public async removeDeployedHost(nginxHostArg: NginxHost) {
if (this.hostCandidates.isEmpty()) {
this.deployedHosts.forEach(hostArg => {
this.hostCandidates.add(hostArg);
});
}
this.hostCandidates.remove(nginxHostArg);
this.deploy();
}
/**
* check wether there has been a diverging host configuration
* this function will only redeploy the nginx configuration in case there has been a change
*/
private async areHostsDiverged(): Promise<boolean> {
let hostCounter = 0;
let unfoundHosts = 0;
await this.hostCandidates.forEach(async hostCandidateArg => {
let foundHost = false;
await this.deployedHosts.forEach(async deployedHostArg => {
if (
hostCandidateArg.hostName === deployedHostArg.hostName &&
hostCandidateArg.destination === deployedHostArg.destination
) {
hostCounter++;
foundHost = true;
}
});
if (!foundHost) {
unfoundHosts++;
}
});
return (
this.deployedHosts.getArray().length !== this.hostCandidates.getArray().length ||
hostCounter !== this.deployedHosts.getArray().length ||
unfoundHosts !== 0
);
}
/**
* deploy the current stack and restart nginx
*/
public async deploy() {
if (await this.areHostsDiverged()) {
this.logger.log('ok', `hosts have diverged, trigger config deployment and nginx reload!`);
this.deployedHosts.wipe();
this.deployedHosts.addArray(this.hostCandidates.getArray());
this.hostCandidates.wipe();
// write base config
plugins.smartfile.fs.ensureDirSync(paths.nginxConfigDirPath);
plugins.smartfile.memory.toFsSync(snippets.getBaseConfigString(), paths.nginxConfFile);
// deploy hosts
plugins.smartfile.fs.ensureEmptyDirSync(paths.nginxHostDirPath);
for (const host of this.deployedHosts.getArray()) {
await host.deploy();
this.logger.log('info', `Host ${host.hostName} deployed!`);
}
this.nginxProcess.reloadConfig();
} else {
this.logger.log('info', `hosts have not diverged, skipping nginx reload`);
this.hostCandidates.wipe();
}
}
}

View File

@ -1,10 +1,9 @@
import * as plugins from "./smartnginx.plugins" import * as plugins from './smartnginx.plugins';
// directories // directories
export let packageBase = plugins.path.join(__dirname,"../"); export const packageBase = plugins.path.join(__dirname, '../');
export let nginxConfigBase = plugins.path.join(packageBase,"nginxconfig"); export const nginxConfigDirPath = plugins.path.join(packageBase, 'nginxconfig');
export let nginxHostFileBase = plugins.path.join(nginxConfigBase,"hosts"); export const nginxHostDirPath = plugins.path.join(nginxConfigDirPath, 'hosts');
export let nginxCertBase = plugins.path.join(nginxConfigBase,"cert");
// files // files
export let nginxConfFile = plugins.path.join(nginxConfigBase,"nginx.conf"); export const nginxConfFile = plugins.path.join(nginxConfigDirPath, 'nginx.conf');

View File

@ -1,9 +1,14 @@
import "typings-global"; // native
export import beautylog = require("beautylog"); import * as path from 'path';
export import cert = require("cert");
export import childProcess = require("child_process"); export { path };
export import path = require("path");
export import q = require("q"); // @pushrocks scope
export import shelljs = require("shelljs"); import * as lik from '@pushrocks/lik';
export import smartfile = require("smartfile"); import * as smartlog from '@pushrocks/smartlog';
export import smartstring = require("smartstring"); import * as smartpromise from '@pushrocks/smartpromise';
import * as smartshell from '@pushrocks/smartshell';
import * as smartfile from '@pushrocks/smartfile';
import * as smartstring from '@pushrocks/smartstring';
export { lik, smartlog, smartpromise, smartshell, smartfile, smartstring };

View File

@ -1,10 +1,10 @@
import * as plugins from "./smartnginx.plugins"; import * as plugins from './smartnginx.plugins';
import * as paths from "./smartnginx.paths"; import * as paths from './smartnginx.paths';
export let getBaseConfigString = () => { export let getBaseConfigString = () => {
let baseConfig = plugins.smartstring.indent.normalize(` let baseConfig = plugins.smartstring.indent.normalize(`
user www-data; user www-data;
worker_processes auto; worker_processes auto;
pid /run/nginx.pid; pid /run/nginx/nginx.pid;
events { events {
worker_connections 768; worker_connections 768;
@ -63,16 +63,15 @@ export let getBaseConfigString = () => {
# Virtual Host Configs # Virtual Host Configs
## ##
include ${paths.nginxHostFileBase}/*.conf; include ${paths.nginxHostDirPath}/*.conf;
include /etc/nginx/sites-enabled/*; include /etc/nginx/sites-enabled/*;
} }
daemon off; daemon off;
`); `);
return baseConfig; return baseConfig;
} };
export let getHostConfigString = (hostNameArg: string, destinationIpArg: string) => {
export let getHostConfigString = (hostNameArg:string,destinationIpArg:string) => {
let hostConfig = plugins.smartstring.indent.normalize(` let hostConfig = plugins.smartstring.indent.normalize(`
upstream ${hostNameArg} { upstream ${hostNameArg} {
server ${destinationIpArg}; server ${destinationIpArg};
@ -87,18 +86,19 @@ export let getHostConfigString = (hostNameArg:string,destinationIpArg:string) =>
server { server {
listen *:443 ssl; listen *:443 ssl;
server_name ${hostNameArg}; server_name ${hostNameArg};
ssl_certificate ${paths.nginxCertBase}/${hostNameArg}/fullchain.pem; ssl_certificate ${paths.nginxHostDirPath}/${hostNameArg}.public.pem;
ssl_certificate_key ${paths.nginxCertBase}/${hostNameArg}/privkey.pem; ssl_certificate_key ${paths.nginxHostDirPath}/${hostNameArg}.private.pem;
location / { location / {
proxy_http_version 1.1;
keepalive 100;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://${hostNameArg}; proxy_pass http://${hostNameArg};
include /etc/nginx/proxy_params;
}
location ~ /\.git {
deny all;
} }
} }
`); `);
return hostConfig; return hostConfig;
}; };

17
tslint.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}