tunli 0.0.19

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.
Files changed (63) hide show
  1. package/LICENSE.md +595 -0
  2. package/README.md +135 -0
  3. package/bin/tunli +11 -0
  4. package/client.js +31 -0
  5. package/package.json +51 -0
  6. package/src/cli-app/Dashboard.js +146 -0
  7. package/src/cli-app/Screen.js +135 -0
  8. package/src/cli-app/elements/ElementNode.js +97 -0
  9. package/src/cli-app/elements/Line.js +21 -0
  10. package/src/cli-app/elements/List/List.js +227 -0
  11. package/src/cli-app/elements/List/ListCell.js +83 -0
  12. package/src/cli-app/elements/List/ListColumn.js +52 -0
  13. package/src/cli-app/elements/List/ListRow.js +118 -0
  14. package/src/cli-app/elements/Row.js +38 -0
  15. package/src/cli-app/helper/utils.js +42 -0
  16. package/src/commands/Action/addDelValuesAction.js +56 -0
  17. package/src/commands/CommandAuth.js +32 -0
  18. package/src/commands/CommandClearAll.js +27 -0
  19. package/src/commands/CommandConfig.js +57 -0
  20. package/src/commands/CommandHTTP.js +131 -0
  21. package/src/commands/CommandInvite.js +38 -0
  22. package/src/commands/CommandRefresh.js +35 -0
  23. package/src/commands/CommandRegister.js +48 -0
  24. package/src/commands/Option/DeleteOption.js +6 -0
  25. package/src/commands/Option/SelectConfigOption.js +52 -0
  26. package/src/commands/SubCommand/AllowDenyCidrCommand.js +28 -0
  27. package/src/commands/SubCommand/HostCommand.js +22 -0
  28. package/src/commands/SubCommand/PortCommand.js +20 -0
  29. package/src/commands/helper/AliasResolver.js +13 -0
  30. package/src/commands/helper/BindArgs.js +53 -0
  31. package/src/commands/helper/SharedArg.js +32 -0
  32. package/src/commands/utils.js +96 -0
  33. package/src/config/ConfigAbstract.js +318 -0
  34. package/src/config/ConfigManager.js +70 -0
  35. package/src/config/GlobalConfig.js +14 -0
  36. package/src/config/GlobalLocalShardConfigAbstract.js +76 -0
  37. package/src/config/LocalConfig.js +7 -0
  38. package/src/config/PropertyConfig.js +122 -0
  39. package/src/config/SystemConfig.js +31 -0
  40. package/src/core/FS/utils.js +60 -0
  41. package/src/core/Ref.js +70 -0
  42. package/src/lib/Flow/getCurrentIp.js +18 -0
  43. package/src/lib/Flow/getLatestVersion.js +13 -0
  44. package/src/lib/Flow/proxyUrl.js +32 -0
  45. package/src/lib/Flow/validateAuthToken.js +19 -0
  46. package/src/lib/HttpClient.js +61 -0
  47. package/src/lib/defs.js +10 -0
  48. package/src/net/IPV4.js +139 -0
  49. package/src/net/http/IncomingMessage.js +92 -0
  50. package/src/net/http/ServerResponse.js +126 -0
  51. package/src/net/http/TunliRequest.js +1 -0
  52. package/src/net/http/TunliResponse.js +1 -0
  53. package/src/net/http/TunnelRequest.js +177 -0
  54. package/src/net/http/TunnelResponse.js +119 -0
  55. package/src/tunnel-client/TunnelClient.js +136 -0
  56. package/src/utils/arrayFunctions.js +45 -0
  57. package/src/utils/checkFunctions.js +161 -0
  58. package/src/utils/cliFunctions.js +62 -0
  59. package/src/utils/createRequest.js +12 -0
  60. package/src/utils/httpFunction.js +23 -0
  61. package/src/utils/npmFunctions.js +27 -0
  62. package/src/utils/stringFunctions.js +34 -0
  63. package/types/index.d.ts +112 -0
@@ -0,0 +1,227 @@
1
+ import {isRef} from "#src/core/Ref";
2
+ import blessed from 'blessed';
3
+ import {ListColumn} from "#src/cli-app/elements/List/ListColumn";
4
+ import {ListCell} from "#src/cli-app/elements/List/ListCell";
5
+ import {ListRow} from "#src/cli-app/elements/List/ListRow";
6
+ import {ElementNode} from "#src/cli-app/elements/ElementNode";
7
+ import {padEndIgnoreControlChars} from "#src/utils/stringFunctions";
8
+
9
+ export class List extends ElementNode {
10
+
11
+ /**
12
+ * @type {Screen}
13
+ */
14
+ #screen
15
+ #box
16
+ /**
17
+ * @type {ListRow[]}
18
+ */
19
+ #rows = []
20
+ /**
21
+ * @type {ListColumn[]}
22
+ */
23
+ #columns = []
24
+ #needsUpdate = false
25
+
26
+ /**
27
+ * @type {cliListOption}
28
+ */
29
+ #options = {}
30
+
31
+ /**
32
+ * @param {cliListOption} [options]
33
+ */
34
+ init(options) {
35
+
36
+ options ??= {}
37
+ this.#options.length = options.length
38
+ this.#options.maxLength = options.maxLength ?? this.#options.length
39
+ this.#options.minLength = options.minLength ?? options.maxLength ?? this.#options.length ?? 0
40
+ this.#options.minWidth = options.minWidth ?? 0
41
+ this.#options.maxWidth = options.maxWidth ?? Infinity
42
+ this.#options.reverse = options.reverse === true
43
+ this.#screen = this.screen
44
+
45
+ this.height = this.#options.minLength
46
+
47
+ this.on('position-top', (val) => this.#box.top = val)
48
+ this.on('height', (val) => this.#box.height = val)
49
+ this.on('delete', () => this.#box.detach())
50
+
51
+ this.#box = blessed.box({
52
+ top: this.positionTop,
53
+ // top: 'center',
54
+ left: 'center',
55
+ width: '100%',
56
+ height: this.#options.minLength,
57
+ content: '',
58
+ tags: true,
59
+ // border: {
60
+ // type: 'line'
61
+ // },
62
+ // style: { // TODO MOVE TO PRESETS
63
+ // fg: 'white',
64
+ // bg: 'blue',
65
+ // border: {
66
+ // fg: '#f0f0f0'
67
+ // },
68
+ // }
69
+ });
70
+
71
+ this.#screen.append(this.#box)
72
+ }
73
+
74
+ /**
75
+ * @param {ListRow} row
76
+ * @return {string}
77
+ */
78
+ #formatRow(row) {
79
+ let contentRow = ``
80
+
81
+ const lastCell = row.lastCell
82
+
83
+ for (const cell of row.cells) {
84
+
85
+ if (cell !== lastCell) {
86
+ const cellWidth = this.#calWidth(cell)
87
+ contentRow += padEndIgnoreControlChars(cell.content, cellWidth - 1, ' ', '...') + ' '
88
+ }
89
+ }
90
+
91
+ contentRow += `${lastCell.content}`
92
+ return contentRow
93
+ }
94
+
95
+
96
+ /**
97
+ * @returns {?ListRow}
98
+ */
99
+ row(...content) {
100
+
101
+ if (this.detached) {
102
+ return null
103
+ }
104
+
105
+ this.#needsUpdate = false
106
+ // ACTUNG ES MÜSSEN SÄMTLICHE REFERNEZNE GELÖSCHT WEWRDEN DELETE CASCADE
107
+ const cells = content.map((x, index) => {
108
+ this.#columns[index] ??= (new ListColumn()).on('width-change', (newValue, oldValue) => {
109
+ this.#needsUpdate = true
110
+ })
111
+ return new ListCell(x, this.#columns[index], index)
112
+ })
113
+
114
+ for (let i = 0; i < cells.length; i++) {
115
+ if (isRef(content[i])) {
116
+ content[i].on('update', (value) => {
117
+ const cell = cells[i]
118
+ const widthBefore = cell.column.width
119
+ cell.content = value
120
+ const needsUpdate = widthBefore !== cell.column.width
121
+
122
+ if (needsUpdate) {
123
+ this.#render(needsUpdate)
124
+ } else {
125
+ this.#updateRow(cell.row)
126
+ }
127
+ this.#screen.render()
128
+ })
129
+ }
130
+ }
131
+
132
+ const rowNodeBefore = this.#rows[this.#rows.length - 1]
133
+ const rowNew = new ListRow(cells, rowNodeBefore)
134
+
135
+ this.#rows.push(rowNew)
136
+
137
+ let append = true
138
+
139
+ if (this.#options.maxLength) {
140
+ this.#needsUpdate = true
141
+ const delRows = this.#rows.splice(0, this.#rows.length - this.#options.maxLength)
142
+ append = !delRows.length
143
+ for (const del of delRows) {
144
+ del.delete()
145
+ }
146
+ }
147
+
148
+ rowNew.on('delete', (/** ListRow */ row) => {
149
+ this.#box.deleteLine(row.lineNumber)
150
+
151
+ if (this.#rows[row.lineNumber] === row) {
152
+ delete this.#rows[row.lineNumber]
153
+ }
154
+ }).on('hide', () => {
155
+ this.#render(true)
156
+ this.screen.render()
157
+ }).on('show', () => {
158
+ this.#render(true)
159
+ this.screen.render()
160
+ })
161
+
162
+ this.#render(this.#needsUpdate, append)
163
+
164
+ return rowNew
165
+ }
166
+
167
+ /**
168
+ * @param {ListRow} row
169
+ */
170
+ #updateRow(row) {
171
+ let spacing = 1
172
+
173
+ this.#box.setLine(row.lineNumber, this.#formatRow(row))
174
+ // this.#screen.render()
175
+ }
176
+
177
+ /**
178
+ * @param {ListCell} cell
179
+ */
180
+ #calWidth(cell) {
181
+ const spacing = 1
182
+ let set = this.#options.minWidth
183
+ let max = this.#options.maxWidth
184
+
185
+ if (Array.isArray(this.#options.minWidth)) {
186
+ set = this.#options.minWidth[cell.index]
187
+ }
188
+ if (Array.isArray(this.#options.maxWidth)) {
189
+ max = this.#options.maxWidth[cell.index]
190
+ }
191
+
192
+ return Math.min(
193
+ max ?? Infinity,
194
+ Math.max(
195
+ set ?? 0,
196
+ cell.column.width + spacing
197
+ ))
198
+ }
199
+
200
+ #render(needsUpdate, append) {
201
+
202
+ this.#rows = this.#rows.filter(x => !x.deleted)
203
+ const rows = this.#rows
204
+ const visibleRows = this.#rows.filter(row => !row.hidden)
205
+
206
+ if (append) {
207
+ this.#box.insertLine(visibleRows.length - 1, this.#formatRow(rows[rows.length - 1]))
208
+ }
209
+
210
+ if (needsUpdate) {
211
+ for (let i = 0; i < visibleRows.length; i++) {
212
+ const rowId = this.#options.reverse ? visibleRows.length - 1 - i : i
213
+ this.#box.setLine(i, this.#formatRow(visibleRows[rowId]))
214
+ }
215
+ }
216
+
217
+ this.height = Math.max(visibleRows.length + 2, this.#options.minLength + 2)// border top bottom
218
+
219
+ // Clear empty lines
220
+
221
+ for (let i = visibleRows.length; i < this.height; i++) {
222
+ this.#box.setLine(i, '')
223
+ }
224
+
225
+ this.screen.updateElement(this)
226
+ }
227
+ }
@@ -0,0 +1,83 @@
1
+ import {isRef} from "#src/core/Ref";
2
+ import {removeControlChars} from "#src/utils/stringFunctions";
3
+
4
+ export class ListCell {
5
+ /**
6
+ * @type {string}
7
+ */
8
+ #content
9
+ /**
10
+ * @type {ListColumn}
11
+ */
12
+ #column
13
+
14
+ /**
15
+ * @type {ListRow}
16
+ */
17
+ #row
18
+
19
+ #contentLength
20
+
21
+ /**
22
+ *
23
+ * @param content
24
+ * @param {ListColumn} column
25
+ */
26
+ constructor(content, column, index) {
27
+
28
+ this.index = index
29
+ if (isRef(content)) {
30
+ content = content.value
31
+ }
32
+
33
+ content ??= ''
34
+
35
+ const toc = typeof content
36
+
37
+ if (toc === 'number') {
38
+ content = content.toString()
39
+ } else if (toc !== 'string') {
40
+ if (content) {
41
+ content = content.toString()
42
+ } else {
43
+ content = 'INVALID_STRING'
44
+ }
45
+ }
46
+
47
+ this.#contentLength = removeControlChars(content).length
48
+ this.#content = content
49
+ this.#column = column
50
+ this.#column.push(this)
51
+ }
52
+
53
+ get row() {
54
+ return this.#row
55
+ }
56
+
57
+ set row(row) {
58
+ this.#row = row
59
+ }
60
+
61
+ get contentLength() {
62
+ return this.#contentLength
63
+ }
64
+
65
+ get content() {
66
+ return this.#content;
67
+ }
68
+
69
+ set content(value) {
70
+ this.#content = value;
71
+ }
72
+
73
+ /**
74
+ * @return {ListColumn}
75
+ */
76
+ get column() {
77
+ return this.#column
78
+ }
79
+
80
+ delete() {
81
+ this.column.deleteCell(this)
82
+ }
83
+ }
@@ -0,0 +1,52 @@
1
+ import EventEmitter from "node:events";
2
+ import {arrayRemoveEntry} from "#src/utils/arrayFunctions";
3
+
4
+ export class ListColumn extends EventEmitter {
5
+
6
+ /**
7
+ *
8
+ * @type {ListCell[]}
9
+ */
10
+ #cells = []//new Set()
11
+
12
+ #width
13
+ /**
14
+ * @return {number}
15
+ */
16
+ get width() {
17
+ // CACHING VIA UPDATE EVENT FROM CELLS?
18
+ const tmp = Math.max(...this.#cells.map(x => x.contentLength))
19
+
20
+ if (tmp < 0) {
21
+ return 0
22
+ }
23
+
24
+ return tmp
25
+ }
26
+
27
+ /**
28
+ * @param {ListCell} cell
29
+ */
30
+ deleteCell(cell) {
31
+ const before = this.width
32
+ this.#cells = arrayRemoveEntry(this.#cells, cell)
33
+ const now = this.width
34
+ if (now !== before) {
35
+ this.emit('width-change', now, before)
36
+ }
37
+ }
38
+
39
+ /**
40
+ *
41
+ * @param {ListCell} cell
42
+ */
43
+ push(cell) {
44
+ const before = this.width
45
+ this.#cells.push(cell)//, cell.content.length)
46
+ const now = this.width
47
+
48
+ if (now !== before) {
49
+ this.emit('width-change', now, before)
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,118 @@
1
+ import EventEmitter from "node:events";
2
+ import {ListCell} from "#src/cli-app/elements/List/ListCell";
3
+ import {isRef} from "#src/core/Ref";
4
+
5
+ export class ListRow extends EventEmitter {
6
+
7
+ /**
8
+ * @param {ListCell[]} cells
9
+ */
10
+ #cells
11
+
12
+ /**
13
+ * @type {number}
14
+ */
15
+ #lineNumber
16
+ /**
17
+ * @type {ListRow}
18
+ */
19
+ #_rowBefore
20
+ #deleted = false
21
+ #hidden = false
22
+
23
+ /**
24
+ * @param {ListCell[]} cells
25
+ */
26
+ constructor(cells, rowNodeBefore) {
27
+ super()
28
+ this.#cells = cells
29
+ this.#rowBefore = rowNodeBefore
30
+
31
+ this.#rowBefore?.on('delete', (rowBefore) => {
32
+ this.#rowBefore = rowBefore?.#rowBefore
33
+ })
34
+
35
+ cells.forEach(cell => cell.row = this)
36
+ }
37
+
38
+ /**
39
+ * @returns {ListRow}
40
+ */
41
+ get #rowBefore() {
42
+ if (this.#_rowBefore?.hidden) {
43
+ return this.#_rowBefore.#rowBefore
44
+ }
45
+
46
+ return this.#_rowBefore
47
+ }
48
+
49
+ /**
50
+ * @param {ListRow} rowBefore
51
+ */
52
+ set #rowBefore(rowBefore) {
53
+ this.#_rowBefore = rowBefore
54
+ }
55
+
56
+ get length() {
57
+ return this.#cells.length
58
+ }
59
+
60
+ get lineNumber() {
61
+ if (this.#rowBefore) {
62
+ return this.#rowBefore?.lineNumber + 1
63
+ }
64
+ return 0
65
+ }
66
+
67
+ get deleted() {
68
+ return this.#deleted
69
+ }
70
+
71
+ /**
72
+ * @return {ListCell[]}
73
+ */
74
+ get cells() {
75
+ return this.#cells
76
+ }
77
+
78
+ /**
79
+ * @return {ListCell}
80
+ */
81
+ get lastCell() {
82
+ return this.#cells[this.#cells.length - 1]
83
+ }
84
+
85
+ get hidden() {
86
+ return this.#hidden
87
+ }
88
+
89
+ set hidden(hidden) {
90
+
91
+ if (this.#hidden === hidden) {
92
+ return
93
+ }
94
+
95
+ this.#hidden = hidden
96
+ this.emit(hidden ? 'hide' : 'show')
97
+ }
98
+
99
+ if(renderIfCallback) {
100
+ const result = renderIfCallback()
101
+ if (isRef(result)) {
102
+ result.on('update', (val) => this.hidden = !val)
103
+ this.hidden = !result.value
104
+ } else {
105
+ this.hidden = !result
106
+ }
107
+ return this
108
+ }
109
+
110
+ delete() {
111
+ this.#deleted = true
112
+
113
+ for (const cell of this.#cells) {
114
+ cell.delete()
115
+ }
116
+ this.emit('delete', this)
117
+ }
118
+ }
@@ -0,0 +1,38 @@
1
+ import blessed from 'blessed';
2
+ import {ElementNode} from "#src/cli-app/elements/ElementNode";
3
+
4
+ export class Row extends ElementNode {
5
+
6
+ #content
7
+ #row
8
+
9
+ set content(content) {
10
+ this.height = content.split("\n").length
11
+ this.#row.content = content
12
+ this.screen.updateElement(this)
13
+ }
14
+
15
+ /**
16
+ * @param {string|Ref} content
17
+ */
18
+ init(content, {top, right}) {
19
+
20
+ this.offsetTop = top
21
+
22
+ this.#content = content
23
+ // this.#rowBefore = rowNodeBefore
24
+ this.#row = blessed.text({
25
+ top: this.positionTop,
26
+ right,
27
+ content
28
+ })
29
+
30
+ this.height = 1
31
+
32
+ this.on('position-top', (val) => this.#row.top = val)
33
+ this.on('height', (val) => this.#row.height = val)
34
+ this.on('delete', (val) => this.#row.detach())
35
+
36
+ this.screen.append(this.#row)
37
+ }
38
+ }
@@ -0,0 +1,42 @@
1
+ import blessed from 'blessed';
2
+ import {Screen} from "#src/cli-app/Screen";
3
+ import {isRef, ref} from "#src/core/Ref";
4
+
5
+ /**
6
+ * @param {string} [title]
7
+ * @returns {Screen}
8
+ */
9
+ export const createScreen = (title) => {
10
+ const screen = blessed.screen({
11
+ smartCSR: true,
12
+ title
13
+ });
14
+ return new Screen(screen)
15
+ }
16
+
17
+ /**
18
+ * @param {string|Ref|number} values
19
+ * @returns {Ref}
20
+ */
21
+ export const concat = (...values) => {
22
+
23
+ const update = () => {
24
+ finalValue.value = values.map((x) => {
25
+
26
+
27
+ return x.value
28
+ }).join('')
29
+ }
30
+
31
+ const finalValue = ref()
32
+ values = values.map(value => {
33
+ if (!isRef(value)) {
34
+ return ref(value)
35
+ } else {
36
+ value.on('update', update)
37
+ }
38
+ return value
39
+ })
40
+ update()
41
+ return finalValue
42
+ }
@@ -0,0 +1,56 @@
1
+ import {arrayMerge, arraySub, arrayUnique} from "#src/utils/arrayFunctions";
2
+
3
+ /**
4
+ * @param configKey
5
+ * @param {Ref} configRef
6
+ * @returns {(function(*, {add: *, del: *}): void)|*}
7
+ */
8
+ export const addDelValuesAction = (configKey, configRef) => {
9
+
10
+ return (argValues, {add, del}) => {
11
+
12
+ /** @type {LocalConfig|GlobalConfig} */
13
+ const config = configRef.value
14
+
15
+ if (!argValues.length && !del && !add) {
16
+ console.log(config[configKey].join(', '))
17
+ return
18
+ }
19
+
20
+ if (!argValues.length) {
21
+ argValues = config[configKey]
22
+ }
23
+
24
+ argValues = arrayUnique(arraySub(arrayMerge(argValues, add), del))
25
+ config[configKey] = argValues
26
+ config.save()
27
+ }
28
+ }
29
+
30
+ /**
31
+ * @param configKey
32
+ * @param configRef
33
+ * @returns {(function(*, {add: *, del: *}): void)|*}
34
+ */
35
+ export const addGetDeleteValueAction = (configKey, configRef) => {
36
+
37
+ return (value, opts) => {
38
+
39
+ /** @type {LocalConfig|GlobalConfig} */
40
+ const config = configRef.value
41
+
42
+ if (opts.del) {
43
+ config.del(configKey)
44
+ config.save()
45
+ return;
46
+ }
47
+
48
+ if (value === undefined) {
49
+ console.log(config[configKey])
50
+ return
51
+ }
52
+
53
+ config[configKey] = value
54
+ config.save()
55
+ }
56
+ }
@@ -0,0 +1,32 @@
1
+ import {Command} from "commander";
2
+ import {addExample, extendUsage} from "#commands/utils";
3
+ import {ConfigManager} from "#src/config/ConfigManager";
4
+
5
+ /**
6
+ * @returns {(function(): void)|*}
7
+ */
8
+ const exec = () => {
9
+
10
+ return async (token) => {
11
+ const config = ConfigManager.loadSystem()
12
+ config.authToken = token
13
+ config.save()
14
+ console.log('Done.')
15
+ }
16
+ }
17
+
18
+ /**
19
+ *
20
+ * @param {Command} program
21
+ */
22
+ export const createCommandAuth = (program) => {
23
+
24
+ const cmd = new Command('auth')
25
+ .argument('<token>', 'auth token')
26
+ .action(exec())
27
+
28
+ extendUsage(program, cmd)
29
+ addExample('auth <TOKEN>', 'register this client with given token')
30
+ return cmd
31
+
32
+ }
@@ -0,0 +1,27 @@
1
+ import {searchDirInDirectoryTree} from "#lib/FS/utils";
2
+ import {CONFIG_DIR_NAME} from "#lib/defs";
3
+
4
+ /**
5
+ *
6
+ * @param {Command} program
7
+ * @returns {(function(): void)|*}
8
+ */
9
+ const exec = (program) => {
10
+
11
+ return async() => {
12
+
13
+ let path
14
+ while (null !== (path = searchDirInDirectoryTree(CONFIG_DIR_NAME))) {
15
+ console.log(`remove ${path}`)
16
+ await rm(path, {recursive: true, force: true})
17
+ }
18
+ }
19
+ }
20
+ /**
21
+ * @param {Command} program
22
+ */
23
+ export const addCommandClearAll = (program) => {
24
+ program.command('clear-all')
25
+ .description('Remove all configurations in directory tree including global')
26
+ .action(exec(program))
27
+ }