@cloud-cli/px 1.1.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,8 @@ Reverse proxy to map local services to http(s) subdomains
4
4
 
5
5
  #### Usage
6
6
 
7
+ Use it with [Cloudy](https://github.com/cloud-cli/cli)
8
+
7
9
  ```ts
8
10
  // cloudy.conf.mjs
9
11
 
@@ -14,50 +16,56 @@ export default { proxy };
14
16
 
15
17
  #### API
16
18
 
17
- **Add a certificate**
19
+ **Add a proxy to a local service**
18
20
 
19
21
  ```
20
- cy proxy.addCertificate --domain example.com --certificate @path/to/cert --key @path/to/key
22
+ cy proxy.add --domain "foo.example.com" --target "localhost:1234"
23
+ cy proxy.add --domain "bar.example.com/api" --target "localhost:3000"
21
24
  ```
22
25
 
23
- **Remove a certificate**
26
+ **Remove a proxy**
24
27
 
25
28
  ```
26
- cy proxy.removeCertificate --domain example.com
29
+ cy proxy.remove --domain "foo.example.com" --target "localhost:1234"
30
+ cy proxy.remove --domain "bar.example.com/api" --target "localhost:1234"
27
31
  ```
28
32
 
29
- **Add a proxy to a local service**
33
+ **Get details of a proxy**
30
34
 
31
35
  ```
32
- cy proxy.addProxy --domain "foo.example.com" --target "localhost:1234"
36
+ cy proxy.get --domain "foo.example.com"
33
37
  ```
34
38
 
35
- **Remove a proxy**
39
+ **List proxies**
36
40
 
37
41
  ```
38
- cy proxy.removeProxy --domain "foo.example.com"
42
+ cy proxy.list
39
43
  ```
40
44
 
41
- **Get details of a proxy**
45
+ **List registered domains**
42
46
 
43
47
  ```
44
- cy proxy.getProxy --domain "foo.example.com"
48
+ cy proxy.domains
45
49
  ```
46
50
 
47
- **List proxies**
51
+ **Reload all configurations**
48
52
 
49
53
  ```
50
- cy proxy.getProxyList
54
+ cy proxy.reload
51
55
  ```
52
56
 
53
- **List registered domains**
57
+ ## Certificate file resolution
54
58
 
55
59
  ```
56
- cy proxy.getDomainList
60
+ certificatesFolder = '/etc/letsencrypt/live' or process.env.PX_CERTS_FOLDER
61
+ rootDomain = 'example.com' in 'xyz.example.com'
62
+ cert = certificatesFolder + '/' + rootDomain + '/' + certificateFile
63
+ key = certificatesFolder + '/' + rootDomain + '/' + keyFile
57
64
  ```
58
65
 
59
- **Reload all configurations**
66
+ So xyz.example.com points to:
60
67
 
61
68
  ```
62
- cy proxy.reload
69
+ /etc/letsencrypt/live/example.com/fullchain.pem for certificate
70
+ /etc/letsencrypt/live/example.com/privkey.pem for private key
63
71
  ```
package/dist/index.d.ts CHANGED
@@ -1,12 +1,10 @@
1
- import { Proxy, DomainOption, Certificate } from './proxy-manager.js';
1
+ import { HttpProxy, Domain } from './proxy-manager.js';
2
2
  declare const _default: {
3
- addProxy(options: Proxy): void;
4
- removeProxy(options: DomainOption): void;
5
- getProxy(options: DomainOption): Proxy;
6
- addCertificate(options: Certificate): void;
7
- removeCertificate(options: DomainOption): void;
3
+ add(options: HttpProxy): Promise<unknown>;
4
+ remove(options: HttpProxy): Promise<unknown>;
5
+ get(options: Domain): HttpProxy[];
8
6
  reload(): void;
9
- getDomainList(): string[];
10
- getProxyList(): [string, Proxy][];
7
+ domains(): string[];
8
+ list(): string[];
11
9
  };
12
10
  export default _default;
package/dist/index.js CHANGED
@@ -1,28 +1,22 @@
1
1
  import { ProxyManager } from './proxy-manager.js';
2
2
  const manager = new ProxyManager();
3
3
  export default {
4
- addProxy(options) {
4
+ add(options) {
5
5
  return manager.addProxy(options);
6
6
  },
7
- removeProxy(options) {
7
+ remove(options) {
8
8
  return manager.removeProxy(options);
9
9
  },
10
- getProxy(options) {
11
- return manager.getProxyForDomain(options);
12
- },
13
- addCertificate(options) {
14
- return manager.addCertificate(options);
15
- },
16
- removeCertificate(options) {
17
- return manager.removeCertificate(options);
10
+ get(options) {
11
+ return manager.getProxyListForDomain(options);
18
12
  },
19
13
  reload() {
20
14
  return manager.reloadProxies();
21
15
  },
22
- getDomainList() {
16
+ domains() {
23
17
  return manager.getDomainList();
24
18
  },
25
- getProxyList() {
19
+ list() {
26
20
  return manager.getProxyList();
27
- }
21
+ },
28
22
  };
@@ -1,30 +1,23 @@
1
- export interface DomainOption {
1
+ export interface Domain {
2
2
  domain: string;
3
3
  }
4
- export interface Proxy {
5
- domain: string;
4
+ export interface HttpProxy extends Domain {
6
5
  target: string;
7
6
  }
8
- export interface Certificate {
9
- domain: string;
10
- certificate: string;
11
- key: string;
12
- }
13
7
  export declare class ProxyManager {
14
- private proxyConfiguration;
8
+ private proxyList;
15
9
  private _proxy;
16
10
  private get proxy();
17
- constructor();
18
- addProxy({ domain, target }: Proxy): void;
19
- removeProxy(options: DomainOption): void;
20
- addCertificate({ domain, certificate, key }: Certificate): void;
21
- removeCertificate(options: DomainOption): void;
11
+ addProxy(proxy: HttpProxy): Promise<unknown>;
12
+ removeProxy(options: HttpProxy): Promise<unknown>;
22
13
  getDomainList(): string[];
23
- getProxyForDomain(options: DomainOption): Proxy;
14
+ getProxyList(): string[];
15
+ getProxyListForDomain(options: Domain): HttpProxy[];
24
16
  reloadProxies(): void;
25
- getProxyList(): [string, Proxy][];
17
+ private readProxyList;
18
+ private disconnectDomainAndTarget;
26
19
  private connectDomainToTarget;
27
- private writeFile;
20
+ private findRootDomain;
28
21
  private saveProxies;
29
22
  private closeProxy;
30
23
  }
@@ -1,16 +1,17 @@
1
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
2
- import { join } from "path";
3
- import redbird from "redbird";
4
- const certificatesFolder = join(process.cwd(), "certs");
5
- const configurationFile = join(process.cwd(), "data", "px.json");
6
- const keyExtension = ".key";
7
- const certificateExtension = ".cert";
1
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import redbird from 'redbird';
4
+ const certificatesFolder = process.env.PX_CERTS_FOLDER || '/etc/letsencrypt/live';
5
+ const certificateFile = 'fullchain.pem';
6
+ const keyFile = 'privkey.pem';
7
+ const configurationFile = join(process.cwd(), 'data', 'px.json');
8
+ const domainNotSpecifiedError = new Error('Domain not specified');
9
+ const targetNotSpecifiedError = new Error('Target not specified');
10
+ const notFoundError = new Error('Proxy not found');
11
+ const proxyKey = ({ domain, target }) => `${domain} => ${target}`;
8
12
  export class ProxyManager {
9
13
  constructor() {
10
- this.proxyConfiguration = new Map();
11
- if (!existsSync(certificatesFolder)) {
12
- mkdirSync(certificatesFolder, { recursive: true });
13
- }
14
+ this.proxyList = [];
14
15
  }
15
16
  get proxy() {
16
17
  if (!this._proxy) {
@@ -24,79 +25,91 @@ export class ProxyManager {
24
25
  }
25
26
  return this._proxy;
26
27
  }
27
- addProxy({ domain, target }) {
28
- this.connectDomainToTarget(domain, target);
29
- this.proxyConfiguration.set(domain, { domain, target });
30
- this.saveProxies();
28
+ addProxy(proxy) {
29
+ return new Promise((resolve, reject) => {
30
+ if (!proxy.domain) {
31
+ return reject(domainNotSpecifiedError);
32
+ }
33
+ if (!proxy.target) {
34
+ return reject(targetNotSpecifiedError);
35
+ }
36
+ this.connectDomainToTarget(proxy);
37
+ this.saveProxies();
38
+ resolve(null);
39
+ });
31
40
  }
32
41
  removeProxy(options) {
33
- const { domain } = options;
34
- if (!this.proxyConfiguration.has(domain)) {
35
- return;
36
- }
37
- const { target } = this.proxyConfiguration.get(domain);
38
- this.proxy.unregister(domain, target);
39
- this.proxyConfiguration.delete(domain);
40
- this.saveProxies();
41
- }
42
- addCertificate({ domain, certificate, key }) {
43
- const basePath = join(certificatesFolder, domain);
44
- this.writeFile(basePath + certificateExtension, certificate);
45
- this.writeFile(basePath + keyExtension, key);
46
- }
47
- removeCertificate(options) {
48
- const basePath = join(certificatesFolder, options.domain);
49
- rmSync(basePath + certificateExtension, { force: true });
50
- rmSync(basePath + keyExtension, { force: true });
42
+ return new Promise((resolve, reject) => {
43
+ if (!options.domain) {
44
+ return reject(domainNotSpecifiedError);
45
+ }
46
+ if (!options.target) {
47
+ return reject(targetNotSpecifiedError);
48
+ }
49
+ const proxy = this.proxyList.find((p) => proxyKey(p) === proxyKey(options));
50
+ if (!proxy) {
51
+ return reject(notFoundError);
52
+ }
53
+ this.disconnectDomainAndTarget(proxy);
54
+ this.saveProxies();
55
+ resolve(true);
56
+ });
51
57
  }
52
58
  getDomainList() {
53
- return Array.from(this.proxyConfiguration.keys() || []);
59
+ return this.proxyList.map((p) => p.domain);
54
60
  }
55
- getProxyForDomain(options) {
56
- return this.proxyConfiguration.get(options.domain);
61
+ getProxyList() {
62
+ return this.proxyList.map(proxyKey);
63
+ }
64
+ getProxyListForDomain(options) {
65
+ return this.proxyList.filter((p) => p.domain === options.domain);
57
66
  }
58
67
  reloadProxies() {
59
68
  this.closeProxy();
60
- this.proxyConfiguration.clear();
61
- const proxyList = this.getProxyList();
62
- proxyList.forEach(([key, proxy]) => {
63
- const { domain, target } = proxy;
64
- this.proxyConfiguration.set(key, proxy);
65
- this.connectDomainToTarget(domain, target);
66
- });
69
+ this.proxyList = [];
70
+ this.readProxyList().forEach((proxy) => this.connectDomainToTarget(proxy));
67
71
  }
68
- getProxyList() {
69
- if (!existsSync(configurationFile))
72
+ readProxyList() {
73
+ if (!existsSync(configurationFile)) {
70
74
  return [];
71
- const json = readFileSync(configurationFile, "utf8") || "[]";
72
- const entries = JSON.parse(json);
73
- return entries;
75
+ }
76
+ const json = readFileSync(configurationFile, 'utf8') || '[]';
77
+ return JSON.parse(json);
78
+ }
79
+ disconnectDomainAndTarget(proxy) {
80
+ const { domain, target } = proxy;
81
+ this.proxy.unregister(domain, target);
82
+ this.proxyList = this.proxyList.filter((p) => proxyKey(p) !== proxyKey(proxy));
83
+ }
84
+ connectDomainToTarget(proxy) {
85
+ const { domain, target } = proxy;
86
+ const rootDomain = this.findRootDomain(domain);
87
+ const ssl = { redirect: false };
88
+ if (rootDomain) {
89
+ Object.assign(ssl, {
90
+ redirect: true,
91
+ cert: join(certificatesFolder, rootDomain, certificateFile),
92
+ key: join(certificatesFolder, rootDomain, keyFile),
93
+ });
94
+ }
95
+ this.proxy.register(domain, target, { ssl });
96
+ this.proxyList = this.proxyList.filter((p) => proxyKey(p) !== proxyKey(proxy)).concat(proxy);
74
97
  }
75
- connectDomainToTarget(domain, target) {
76
- const parts = domain.split(".");
77
- let rootDomain = "";
98
+ findRootDomain(domain) {
99
+ let rootDomain = '';
100
+ const parts = domain.split('/')[0].split('.');
78
101
  while (parts.length) {
79
- rootDomain = parts.join(".");
80
- if (existsSync(join(certificatesFolder, rootDomain) + keyExtension))
81
- break;
102
+ rootDomain = parts.join('.');
103
+ if (existsSync(join(certificatesFolder, rootDomain, certificateFile))) {
104
+ return rootDomain;
105
+ }
82
106
  parts.shift();
83
107
  }
84
- if (!rootDomain) {
85
- throw new Error("Certificate not found for " + domain);
86
- }
87
- this.proxy.register(domain, target, {
88
- ssl: {
89
- cert: join(certificatesFolder, rootDomain) + certificateExtension,
90
- key: join(certificatesFolder, rootDomain) + keyExtension,
91
- },
92
- });
93
- }
94
- writeFile(path, content) {
95
- writeFileSync(path, content);
108
+ return '';
96
109
  }
97
110
  saveProxies() {
98
- const proxies = Array.from(this.proxyConfiguration.entries());
99
- this.writeFile(configurationFile, JSON.stringify(proxies));
111
+ const proxies = Array.from(this.proxyList.entries());
112
+ writeFileSync(configurationFile, JSON.stringify(proxies));
100
113
  }
101
114
  closeProxy() {
102
115
  this.proxy.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-cli/px",
3
- "version": "1.1.2",
3
+ "version": "2.0.0",
4
4
  "description": "Reverse Proxy",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",