@cloud-cli/px 1.1.2 → 2.0.1
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 +24 -16
- package/dist/index.d.ts +6 -8
- package/dist/index.js +7 -13
- package/dist/proxy-manager.d.ts +10 -17
- package/dist/proxy-manager.js +80 -68
- package/package.json +1 -1
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
|
|
19
|
+
**Add a proxy to a local service**
|
|
18
20
|
|
|
19
21
|
```
|
|
20
|
-
cy proxy.
|
|
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
|
|
26
|
+
**Remove a proxy**
|
|
24
27
|
|
|
25
28
|
```
|
|
26
|
-
cy proxy.
|
|
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
|
-
**
|
|
33
|
+
**Get details of a proxy**
|
|
30
34
|
|
|
31
35
|
```
|
|
32
|
-
cy proxy.
|
|
36
|
+
cy proxy.get --domain "foo.example.com"
|
|
33
37
|
```
|
|
34
38
|
|
|
35
|
-
**
|
|
39
|
+
**List proxies**
|
|
36
40
|
|
|
37
41
|
```
|
|
38
|
-
cy proxy.
|
|
42
|
+
cy proxy.list
|
|
39
43
|
```
|
|
40
44
|
|
|
41
|
-
**
|
|
45
|
+
**List registered domains**
|
|
42
46
|
|
|
43
47
|
```
|
|
44
|
-
cy proxy.
|
|
48
|
+
cy proxy.domains
|
|
45
49
|
```
|
|
46
50
|
|
|
47
|
-
**
|
|
51
|
+
**Reload all configurations**
|
|
48
52
|
|
|
49
53
|
```
|
|
50
|
-
cy proxy.
|
|
54
|
+
cy proxy.reload
|
|
51
55
|
```
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
## Certificate file resolution
|
|
54
58
|
|
|
55
59
|
```
|
|
56
|
-
|
|
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
|
-
|
|
66
|
+
So xyz.example.com points to:
|
|
60
67
|
|
|
61
68
|
```
|
|
62
|
-
|
|
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 {
|
|
1
|
+
import { HttpProxy, Domain } from './proxy-manager.js';
|
|
2
2
|
declare const _default: {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
4
|
+
add(options) {
|
|
5
5
|
return manager.addProxy(options);
|
|
6
6
|
},
|
|
7
|
-
|
|
7
|
+
remove(options) {
|
|
8
8
|
return manager.removeProxy(options);
|
|
9
9
|
},
|
|
10
|
-
|
|
11
|
-
return manager.
|
|
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
|
-
|
|
16
|
+
domains() {
|
|
23
17
|
return manager.getDomainList();
|
|
24
18
|
},
|
|
25
|
-
|
|
19
|
+
list() {
|
|
26
20
|
return manager.getProxyList();
|
|
27
|
-
}
|
|
21
|
+
},
|
|
28
22
|
};
|
package/dist/proxy-manager.d.ts
CHANGED
|
@@ -1,30 +1,23 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface Domain {
|
|
2
2
|
domain: string;
|
|
3
3
|
}
|
|
4
|
-
export interface
|
|
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
|
|
8
|
+
private proxyList;
|
|
15
9
|
private _proxy;
|
|
16
10
|
private get proxy();
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
14
|
+
getProxyList(): string[];
|
|
15
|
+
getProxyListForDomain(options: Domain): HttpProxy[];
|
|
24
16
|
reloadProxies(): void;
|
|
25
|
-
|
|
17
|
+
private readProxyList;
|
|
18
|
+
private disconnectDomainAndTarget;
|
|
26
19
|
private connectDomainToTarget;
|
|
27
|
-
private
|
|
20
|
+
private findRootDomain;
|
|
28
21
|
private saveProxies;
|
|
29
22
|
private closeProxy;
|
|
30
23
|
}
|
package/dist/proxy-manager.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { existsSync,
|
|
2
|
-
import { join } from
|
|
3
|
-
import redbird from
|
|
4
|
-
const certificatesFolder =
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
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.
|
|
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,90 @@ export class ProxyManager {
|
|
|
24
25
|
}
|
|
25
26
|
return this._proxy;
|
|
26
27
|
}
|
|
27
|
-
addProxy(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
59
|
+
return this.proxyList.map((p) => p.domain);
|
|
54
60
|
}
|
|
55
|
-
|
|
56
|
-
return this.
|
|
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.
|
|
61
|
-
|
|
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
|
-
|
|
69
|
-
if (!existsSync(configurationFile))
|
|
72
|
+
readProxyList() {
|
|
73
|
+
if (!existsSync(configurationFile)) {
|
|
70
74
|
return [];
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
return
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
81
|
-
|
|
102
|
+
rootDomain = parts.join('.');
|
|
103
|
+
if (existsSync(join(certificatesFolder, rootDomain, certificateFile))) {
|
|
104
|
+
return rootDomain;
|
|
105
|
+
}
|
|
82
106
|
parts.shift();
|
|
83
107
|
}
|
|
84
|
-
|
|
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
|
-
|
|
99
|
-
this.writeFile(configurationFile, JSON.stringify(proxies));
|
|
111
|
+
writeFileSync(configurationFile, JSON.stringify(this.proxyList));
|
|
100
112
|
}
|
|
101
113
|
closeProxy() {
|
|
102
114
|
this.proxy.close();
|