@cloud-cli/px 4.0.0 → 4.1.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
@@ -16,20 +16,24 @@ export default { proxy };
16
16
 
17
17
  ## API
18
18
 
19
+ All proxies have the following properties:
20
+
21
+ | Options | Description |
22
+ | --------------- | ---------------------------------------------------------------------------------------------------- |
23
+ | `domain` | Required. entrypoint for reverse proxy |
24
+ | `target` | Required. Any target address, can be local or remote |
25
+ | `cors` | handle CORS request on proxy level. See notes below! |
26
+ | `redirect` | if request comes as http, redirect to https |
27
+ | `redirectUrl` | if provided, issues a `Location` header and terminates with status 302 instead of proxying a request |
28
+ | `headers` | comma separated headers to set in the outbound connection, e.g. authorisation headers |
29
+ | `authorization` | matches incoming request headers against this value. If strings match, requests can continue. |
30
+
19
31
  **Add a proxy to a local service**
20
32
 
21
33
  ```bash
22
34
  cy proxy.add --domain "foo.example.com" --target "http://localhost:1234"
23
35
  ```
24
36
 
25
- All available options:
26
-
27
- - `domain`: entrypoint for reverse proxy
28
- - `target`: any target for requests, can be local or remote
29
- - `cors`: handle CORS request on proxy level. See notes below!
30
- - `redirect`: if request comes as http, redirect to https
31
- - `redirectUrl`: if provided, issues a `Location` header and terminates with status 302 instead of proxying a request
32
-
33
37
  **Remove a proxy**
34
38
 
35
39
  ```bash
@@ -86,7 +90,6 @@ CORS handling on proxy level is convenient, **but a security risk**. Beware:
86
90
  Every request has additional headers:
87
91
 
88
92
  - x-forwarded-for: the host of the original request.
89
- If a proxy is from 'https://foo.example.com' to 'http://localhost:1234', the header will be 'foo.example.com'
93
+ If a proxy is from 'https://foo.example.com' to 'http://localhost:1234', the header will be 'foo.example.com'
90
94
  - x-forwarded-proto: 'http' or 'https', depends on the original request
91
95
  - forwarded: the standard header with the same information as the other two above
92
-
package/dist/index.d.ts CHANGED
@@ -1,24 +1,13 @@
1
1
  import { init } from '@cloud-cli/cli';
2
- import { DomainAndTarget, Domain, Proxy } from './proxy-manager.js';
3
- type ExtraProps = {
4
- _: string[];
5
- };
6
- declare function add(options: Proxy & ExtraProps): Promise<Proxy>;
7
- declare function remove(options: DomainAndTarget): Promise<unknown>;
8
- declare function update(options: Proxy & ExtraProps): Promise<boolean>;
9
- declare function list(): Promise<Proxy[]>;
10
- declare function get(options: Domain): Promise<Proxy[]>;
11
- declare function domains(): Promise<string[]>;
12
- declare function initServer(): Promise<import("@cloud-cli/proxy").ProxyServer>;
13
- declare function reload(): Promise<void>;
2
+ import { DomainAndTarget, DomainName, Proxy, WithOptionalProps } from './types.js';
14
3
  declare const _default: {
15
- add: typeof add;
16
- remove: typeof remove;
17
- list: typeof list;
18
- get: typeof get;
19
- update: typeof update;
20
- domains: typeof domains;
21
- reload: typeof reload;
22
- [init]: typeof initServer;
4
+ add(options: WithOptionalProps<Proxy>): Promise<Proxy>;
5
+ remove(options: WithOptionalProps<DomainAndTarget>): Promise<boolean>;
6
+ update(options: WithOptionalProps<Proxy>): Promise<Proxy>;
7
+ list(filters: Partial<Proxy>): Promise<Proxy[]>;
8
+ get(options: DomainName): Proxy[];
9
+ domains(): Promise<string[]>;
10
+ [init](): Promise<import("@cloud-cli/proxy").ProxyServer>;
11
+ reload(): Promise<void>;
23
12
  };
24
13
  export default _default;
package/dist/index.js CHANGED
@@ -1,36 +1,31 @@
1
1
  import { init, logInfo } from '@cloud-cli/cli';
2
2
  import { ProxyManager } from './proxy-manager.js';
3
3
  const manager = new ProxyManager();
4
- async function add(options) {
5
- const proxy = await manager.addProxy(options);
6
- await manager.reload();
7
- return proxy;
8
- }
9
- async function remove(options) {
10
- const removed = await manager.removeProxy(options);
11
- await manager.reload();
12
- return removed;
13
- }
14
- async function update(options) {
15
- const output = await manager.updateProxy(options);
16
- await manager.reload();
17
- return output;
18
- }
19
- function list() {
20
- return manager.getProxyList();
21
- }
22
- function get(options) {
23
- return manager.getProxyListForDomain(options);
24
- }
25
- function domains() {
26
- return manager.getDomainList();
27
- }
28
- async function initServer() {
29
- await manager.reload();
30
- return manager.server;
31
- }
32
- async function reload() {
33
- logInfo('Reloading proxy server');
34
- return await manager.reload();
35
- }
36
- export default { add, remove, list, get, update, domains, reload, [init]: initServer };
4
+ export default {
5
+ async add(options) {
6
+ return manager.addProxy(options);
7
+ },
8
+ async remove(options) {
9
+ return manager.removeProxy(options);
10
+ },
11
+ async update(options) {
12
+ return await manager.updateProxy(options);
13
+ },
14
+ list(filters) {
15
+ return manager.getProxyList(filters);
16
+ },
17
+ get(options) {
18
+ return manager.getProxyListForDomain(options);
19
+ },
20
+ domains() {
21
+ return manager.getDomainList();
22
+ },
23
+ async [init]() {
24
+ await manager.reload();
25
+ return manager.server;
26
+ },
27
+ async reload() {
28
+ logInfo('Reloading proxy server');
29
+ return await manager.reload();
30
+ },
31
+ };
@@ -1,38 +1,12 @@
1
1
  import { ProxyServer } from '@cloud-cli/proxy';
2
- interface OptionalProps {
3
- host?: string;
4
- _: string[];
5
- }
6
- export interface Domain extends OptionalProps {
7
- domain: string;
8
- }
9
- export interface DomainAndTarget extends Domain {
10
- target: string;
11
- }
12
- export interface Proxy {
13
- domain: string;
14
- target: string;
15
- redirect: boolean;
16
- redirectUrl: string;
17
- headers: string;
18
- authorization: string;
19
- cors: boolean;
20
- }
21
- type KeysOf<T> = {
22
- [K in keyof T]: T[K] extends () => any ? never : K;
23
- }[keyof T];
24
- type ClassProperties<T> = {
25
- [K in KeysOf<T>]: T[K] extends never ? never : T[K];
26
- };
2
+ import type { DomainAndTarget, DomainName, Proxy, WithOptionalProps } from './types.js';
27
3
  export declare class ProxyManager {
28
4
  server: ProxyServer;
29
- addProxy(proxy: ClassProperties<Proxy> & OptionalProps): Promise<Proxy>;
30
- updateProxy(options: ClassProperties<Proxy> & OptionalProps): Promise<boolean>;
31
- removeProxy(options: DomainAndTarget): Promise<unknown>;
5
+ addProxy(proxy: WithOptionalProps<Proxy>): Promise<Proxy>;
6
+ updateProxy(options: WithOptionalProps<Proxy>): Promise<Proxy>;
7
+ removeProxy(options: WithOptionalProps<DomainAndTarget>): Promise<boolean>;
32
8
  getDomainList(): Promise<string[]>;
33
- getProxyList(): Promise<Proxy[]>;
34
- getProxyListForDomain(options: Domain): Promise<Proxy[]>;
9
+ getProxyList(filters?: Partial<Proxy>): Promise<Proxy[]>;
10
+ getProxyListForDomain(options: WithOptionalProps<DomainName>): Proxy[];
35
11
  reload(): Promise<void>;
36
- private findByDomainAndPath;
37
12
  }
38
- export {};
@@ -1,12 +1,20 @@
1
- import { getStorage } from '@cloud-cli/cli';
1
+ import { getStorage, getConfig } from '@cloud-cli/cli';
2
2
  import { ProxyEntry, ProxyServer, ProxySettings } from '@cloud-cli/proxy';
3
+ const defaultOptions = {
4
+ httpPort: 80,
5
+ httpsPort: 443,
6
+ certsFolder: process.env.PX_CERTS_FOLDER || '/etc/letsencrypt/live',
7
+ };
3
8
  const domainNotSpecifiedError = new Error('Domain not specified');
4
9
  const targetNotSpecifiedError = new Error('Target not specified');
5
- const { set, remove, getAll } = getStorage('px');
10
+ const moduleConfig = await getConfig('px', defaultOptions);
11
+ const { set, get, has, remove, getAll } = getStorage('px');
6
12
  const settings = new ProxySettings({
7
- certificatesFolder: process.env.PX_CERTS_FOLDER || '/etc/letsencrypt/live',
13
+ certificatesFolder: moduleConfig.certsFolder,
8
14
  certificateFile: 'fullchain.pem',
9
15
  keyFile: 'privkey.pem',
16
+ httpPort: moduleConfig.httpPort,
17
+ httpsPort: moduleConfig.httpsPort,
10
18
  });
11
19
  const px = new ProxyServer(settings);
12
20
  const emptyProxy = {
@@ -18,20 +26,21 @@ const emptyProxy = {
18
26
  headers: '',
19
27
  authorization: '',
20
28
  };
29
+ const readDomain = (options) => (options.domain = options.domain || options.host || options._[0]);
21
30
  export class ProxyManager {
22
31
  constructor() {
23
32
  this.server = px;
24
33
  }
25
34
  async addProxy(proxy) {
26
- if (!proxy.domain && !proxy.host) {
35
+ readDomain(proxy);
36
+ if (!proxy.domain) {
27
37
  throw domainNotSpecifiedError;
28
38
  }
29
39
  if (!proxy.target && !proxy.redirectUrl) {
30
40
  throw targetNotSpecifiedError;
31
41
  }
32
- const domainAndPath = proxy.domain || proxy.host || proxy._[0];
33
42
  const entry = {
34
- domain: domainAndPath,
43
+ domain: proxy.domain,
35
44
  target: proxy.target,
36
45
  cors: !!proxy.cors,
37
46
  redirect: !!proxy.redirect,
@@ -39,49 +48,59 @@ export class ProxyManager {
39
48
  headers: proxy.headers || '',
40
49
  authorization: proxy.authorization,
41
50
  };
42
- await set(domainAndPath, entry);
51
+ set(proxy.domain, entry);
52
+ await this.reload();
43
53
  return entry;
44
54
  }
45
55
  async updateProxy(options) {
46
- const host = options.domain || options.host || options._[0];
47
- const proxies = await this.findByDomainAndPath(host);
48
- if (!proxies.length) {
49
- proxies.push({ ...emptyProxy, domain: host });
56
+ readDomain(options);
57
+ const host = options.domain;
58
+ let proxy = get(host);
59
+ if (!proxy) {
60
+ proxy = { ...emptyProxy, domain: host };
50
61
  }
51
62
  const properties = ['target', 'cors', 'redirect', 'redirectUrl', 'headers', 'authorization'];
52
- for (const proxy of proxies) {
53
- properties.forEach((p) => p in options && (proxy[p] = options[p]));
54
- set(proxy.domain, proxy);
55
- }
56
- return true;
57
- }
58
- removeProxy(options) {
59
- return new Promise(async (resolve, reject) => {
60
- const host = options.domain || options.host;
61
- if (!host) {
62
- return reject(domainNotSpecifiedError);
63
- }
64
- const proxies = await this.findByDomainAndPath(host);
65
- try {
66
- for (const p of proxies) {
67
- await remove(p.domain);
68
- }
69
- resolve({ found: proxies.length });
70
- }
71
- catch (error) {
72
- reject(error);
63
+ properties.forEach((p) => {
64
+ if (p in options) {
65
+ proxy[p] = options[p];
73
66
  }
74
67
  });
68
+ set(proxy.domain, proxy);
69
+ await this.reload();
70
+ return proxy;
71
+ }
72
+ async removeProxy(options) {
73
+ readDomain(options);
74
+ const host = options.domain;
75
+ if (!host) {
76
+ throw domainNotSpecifiedError;
77
+ }
78
+ if (has(host)) {
79
+ remove(host);
80
+ await this.reload();
81
+ return true;
82
+ }
83
+ return false;
75
84
  }
76
85
  async getDomainList() {
77
- const proxies = await getAll();
86
+ const proxies = getAll();
78
87
  return proxies.map((proxy) => proxy.domain);
79
88
  }
80
- async getProxyList() {
81
- return await getAll();
89
+ async getProxyList(filters = {}) {
90
+ const list = getAll();
91
+ const keys = Object.keys(filters);
92
+ if (!keys.length) {
93
+ return list;
94
+ }
95
+ return keys.reduce((list, key) => {
96
+ const filter = String(filters[key]).toLowerCase();
97
+ return list.filter((p) => String(p[key]).toLowerCase().includes(filter));
98
+ }, list);
82
99
  }
83
100
  getProxyListForDomain(options) {
84
- return this.findByDomainAndPath(options.domain || options.host);
101
+ readDomain(options);
102
+ const all = getAll();
103
+ return all.filter((p) => p.domain === options.domain);
85
104
  }
86
105
  async reload() {
87
106
  const targets = await getAll();
@@ -101,9 +120,4 @@ export class ProxyManager {
101
120
  });
102
121
  px.start();
103
122
  }
104
- async findByDomainAndPath(string) {
105
- const [domain, path = ''] = string.split('/');
106
- const proxies = await getAll();
107
- return proxies.filter(p => p.domain === domain && (!path || domain === path));
108
- }
109
123
  }
@@ -0,0 +1,21 @@
1
+ interface OptionalProps {
2
+ host?: string;
3
+ _: string[];
4
+ }
5
+ export interface DomainName extends OptionalProps {
6
+ domain: string;
7
+ }
8
+ export interface DomainAndTarget extends DomainName {
9
+ target: string;
10
+ }
11
+ export interface Proxy {
12
+ domain: string;
13
+ target: string;
14
+ redirect: boolean;
15
+ redirectUrl: string;
16
+ headers: string;
17
+ authorization: string;
18
+ cors: boolean;
19
+ }
20
+ export type WithOptionalProps<T extends object> = T & OptionalProps;
21
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-cli/px",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "Reverse Proxy",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",