odac 0.9.0 → 1.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.
Files changed (208) hide show
  1. package/.github/workflows/auto-pr-description.yml +0 -2
  2. package/.github/workflows/codeql.yml +46 -0
  3. package/.github/workflows/release.yml +13 -6
  4. package/.github/workflows/test-coverage.yml +10 -9
  5. package/.releaserc.js +9 -6
  6. package/CHANGELOG.md +62 -150
  7. package/CODE_OF_CONDUCT.md +1 -1
  8. package/CONTRIBUTING.md +8 -8
  9. package/LICENSE +21 -661
  10. package/README.md +12 -12
  11. package/SECURITY.md +4 -4
  12. package/bin/odac.js +101 -0
  13. package/{framework/web/candy.js → client/odac.js} +310 -44
  14. package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
  15. package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
  16. package/docs/backend/01-overview/03-development-server.md +11 -11
  17. package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
  18. package/docs/backend/03-config/00-configuration-overview.md +6 -6
  19. package/docs/backend/03-config/01-database-connection.md +1 -1
  20. package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
  21. package/docs/backend/03-config/04-environment-variables.md +20 -20
  22. package/docs/backend/03-config/05-early-hints.md +4 -4
  23. package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
  24. package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
  25. package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
  26. package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
  27. package/docs/backend/04-routing/05-advanced-routing.md +3 -3
  28. package/docs/backend/04-routing/06-error-pages.md +17 -17
  29. package/docs/backend/04-routing/07-cron-jobs.md +13 -13
  30. package/docs/backend/04-routing/08-middleware.md +214 -0
  31. package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
  32. package/docs/backend/04-routing/09-websocket-examples.md +381 -0
  33. package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
  34. package/docs/backend/04-routing/09-websocket.md +298 -0
  35. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
  36. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
  37. package/docs/backend/05-controllers/03-controller-classes.md +19 -19
  38. package/docs/backend/05-forms/01-custom-forms.md +114 -114
  39. package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
  40. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
  41. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
  42. package/docs/backend/07-views/01-the-view-directory.md +1 -1
  43. package/docs/backend/07-views/02-rendering-a-view.md +22 -22
  44. package/docs/backend/07-views/03-template-syntax.md +52 -52
  45. package/docs/backend/07-views/03-variables.md +84 -84
  46. package/docs/backend/07-views/04-request-data.md +57 -57
  47. package/docs/backend/07-views/05-conditionals.md +78 -78
  48. package/docs/backend/07-views/06-loops.md +114 -114
  49. package/docs/backend/07-views/07-translations.md +66 -66
  50. package/docs/backend/07-views/08-backend-javascript.md +103 -103
  51. package/docs/backend/07-views/09-comments.md +71 -71
  52. package/docs/backend/08-database/01-database-connection.md +8 -8
  53. package/docs/backend/08-database/02-using-mysql.md +49 -49
  54. package/docs/backend/09-validation/01-the-validator-service.md +38 -38
  55. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
  56. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
  57. package/docs/backend/10-authentication/03-register.md +12 -12
  58. package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
  59. package/docs/backend/10-authentication/05-session-management.md +10 -10
  60. package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
  61. package/docs/backend/11-mail/01-the-mail-service.md +5 -5
  62. package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
  63. package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
  64. package/docs/frontend/01-overview/01-introduction.md +30 -30
  65. package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
  66. package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
  67. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
  68. package/docs/frontend/03-forms/01-form-handling.md +32 -32
  69. package/docs/frontend/04-api-requests/01-get-post.md +33 -33
  70. package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
  71. package/docs/frontend/06-websocket/00-overview.md +76 -0
  72. package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
  73. package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
  74. package/docs/index.json +49 -11
  75. package/eslint.config.mjs +6 -6
  76. package/{framework/index.js → index.js} +1 -1
  77. package/package.json +14 -39
  78. package/{framework/src → src}/Auth.js +59 -59
  79. package/{framework/src → src}/Config.js +3 -3
  80. package/{framework/src → src}/Lang.js +7 -7
  81. package/{framework/src → src}/Mail.js +5 -5
  82. package/{framework/src → src}/Mysql.js +42 -42
  83. package/src/Odac.js +112 -0
  84. package/{framework/src → src}/Request.js +38 -36
  85. package/{framework/src → src}/Route/Internal.js +116 -116
  86. package/src/Route/Middleware.js +75 -0
  87. package/src/Route.js +621 -0
  88. package/src/Server.js +22 -0
  89. package/{framework/src → src}/Stream.js +11 -3
  90. package/{framework/src → src}/Validator.js +21 -21
  91. package/{framework/src → src}/Var.js +5 -5
  92. package/{framework/src → src}/View/EarlyHints.js +1 -1
  93. package/{framework/src → src}/View/Form.js +69 -69
  94. package/{framework/src → src}/View.js +78 -81
  95. package/src/WebSocket.js +403 -0
  96. package/template/config.json +5 -0
  97. package/{web → template}/controller/page/about.js +6 -6
  98. package/{web → template}/controller/page/index.js +9 -9
  99. package/{web → template}/package.json +4 -5
  100. package/{web → template}/public/assets/css/style.css +4 -4
  101. package/{web → template}/public/assets/js/app.js +6 -6
  102. package/{web → template}/route/www.js +6 -6
  103. package/{web → template}/skeleton/main.html +1 -1
  104. package/{web → template}/view/content/about.html +5 -5
  105. package/{web → template}/view/content/home.html +12 -12
  106. package/template/view/footer/main.html +11 -0
  107. package/{web → template}/view/head/main.html +1 -1
  108. package/{web → template}/view/header/main.html +2 -2
  109. package/test/core/Candy.test.js +58 -58
  110. package/test/core/Commands.test.js +7 -7
  111. package/test/core/Config.test.js +82 -85
  112. package/test/core/Lang.test.js +2 -2
  113. package/test/core/Process.test.js +6 -6
  114. package/test/framework/Route.test.js +56 -37
  115. package/test/framework/View/EarlyHints.test.js +2 -2
  116. package/test/framework/WebSocket.test.js +100 -0
  117. package/test/framework/middleware.test.js +85 -0
  118. package/test/server/Api.test.js +31 -31
  119. package/test/server/DNS.test.js +11 -11
  120. package/test/server/Hub.test.js +497 -0
  121. package/test/server/Mail.account.test_.js +3 -3
  122. package/test/server/Mail.init.test_.js +10 -10
  123. package/test/server/Mail.test_.js +20 -20
  124. package/test/server/SSL.test_.js +54 -54
  125. package/test/server/Server.test.js +39 -39
  126. package/test/server/Service.test_.js +7 -7
  127. package/test/server/Subdomain.test.js +7 -7
  128. package/test/server/Web/Firewall.test.js +87 -87
  129. package/test/server/Web/Proxy.test.js +397 -0
  130. package/test/server/{Web.test_.js → Web.test.js} +137 -205
  131. package/test/server/__mocks__/fs.js +2 -2
  132. package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
  133. package/test/server/__mocks__/index.js +6 -6
  134. package/test/server/__mocks__/testFactories.js +1 -1
  135. package/test/server/__mocks__/testHelpers.js +7 -7
  136. package/.husky/pre-commit +0 -2
  137. package/.kiro/steering/code-style.md +0 -56
  138. package/.kiro/steering/product.md +0 -20
  139. package/.kiro/steering/structure.md +0 -77
  140. package/.kiro/steering/tech.md +0 -87
  141. package/AGENTS.md +0 -84
  142. package/bin/candy +0 -10
  143. package/bin/candypack +0 -10
  144. package/cli/index.js +0 -3
  145. package/cli/src/Cli.js +0 -348
  146. package/cli/src/Connector.js +0 -93
  147. package/cli/src/Monitor.js +0 -416
  148. package/core/Candy.js +0 -87
  149. package/core/Commands.js +0 -239
  150. package/core/Config.js +0 -1094
  151. package/core/Lang.js +0 -52
  152. package/core/Log.js +0 -43
  153. package/core/Process.js +0 -26
  154. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
  155. package/docs/server/01-installation/01-quick-install.md +0 -19
  156. package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
  157. package/docs/server/02-get-started/01-core-concepts.md +0 -7
  158. package/docs/server/02-get-started/02-basic-commands.md +0 -57
  159. package/docs/server/02-get-started/03-cli-reference.md +0 -276
  160. package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
  161. package/docs/server/03-service/01-start-a-new-service.md +0 -57
  162. package/docs/server/03-service/02-delete-a-service.md +0 -48
  163. package/docs/server/04-web/01-create-a-website.md +0 -36
  164. package/docs/server/04-web/02-list-websites.md +0 -9
  165. package/docs/server/04-web/03-delete-a-website.md +0 -29
  166. package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
  167. package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
  168. package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
  169. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
  170. package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
  171. package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
  172. package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
  173. package/docs/server/07-mail/04-change-account-password.md +0 -23
  174. package/framework/src/Candy.js +0 -81
  175. package/framework/src/Route.js +0 -455
  176. package/framework/src/Server.js +0 -15
  177. package/locale/de-DE.json +0 -80
  178. package/locale/en-US.json +0 -79
  179. package/locale/es-ES.json +0 -80
  180. package/locale/fr-FR.json +0 -80
  181. package/locale/pt-BR.json +0 -80
  182. package/locale/ru-RU.json +0 -80
  183. package/locale/tr-TR.json +0 -85
  184. package/locale/zh-CN.json +0 -80
  185. package/server/index.js +0 -5
  186. package/server/src/Api.js +0 -88
  187. package/server/src/DNS.js +0 -940
  188. package/server/src/Hub.js +0 -535
  189. package/server/src/Mail.js +0 -571
  190. package/server/src/SSL.js +0 -180
  191. package/server/src/Server.js +0 -27
  192. package/server/src/Service.js +0 -248
  193. package/server/src/Subdomain.js +0 -64
  194. package/server/src/Web/Firewall.js +0 -170
  195. package/server/src/Web/Proxy.js +0 -134
  196. package/server/src/Web.js +0 -451
  197. package/server/src/mail/imap.js +0 -1091
  198. package/server/src/mail/server.js +0 -32
  199. package/server/src/mail/smtp.js +0 -786
  200. package/test/server/Client.test.js +0 -338
  201. package/test/server/__mocks__/http-proxy.js +0 -105
  202. package/watchdog/index.js +0 -3
  203. package/watchdog/src/Watchdog.js +0 -156
  204. package/web/config.json +0 -5
  205. package/web/view/footer/main.html +0 -11
  206. /package/{framework/src → src}/Env.js +0 -0
  207. /package/{framework/src → src}/Route/Cron.js +0 -0
  208. /package/{framework/src → src}/Token.js +0 -0
@@ -1,416 +0,0 @@
1
- require('../../core/Candy.js')
2
-
3
- const fs = require('fs')
4
- const os = require('os')
5
-
6
- class Monitor {
7
- #current = ''
8
- #domains = []
9
- #height
10
- #logs = {content: [], mtime: null, selected: null, watched: []}
11
- #logging = false
12
- #modules = ['api', 'client', 'config', 'dns', 'hub', 'mail', 'server', 'service', 'ssl', 'subdomain', 'web']
13
- #printing = false
14
- #selected = 0
15
- #services = []
16
- #watch = []
17
- #websites = {}
18
- #width
19
-
20
- constructor() {
21
- process.stdout.write(process.platform === 'win32' ? `title CandyPack Debug\n` : `\x1b]2;CandyPack Debug\x1b\x5c`)
22
- }
23
-
24
- async debug() {
25
- await this.#debug()
26
- setInterval(() => this.#debug(), 250)
27
-
28
- process.stdout.write('\x1b[?25l')
29
- process.stdout.write('\x1b[?1000h')
30
- process.stdin.setRawMode(true)
31
- process.stdin.setEncoding('utf8')
32
- process.stdin.on('data', chunk => {
33
- const buffer = Buffer.from(chunk)
34
- if (buffer.length >= 6 && buffer[0] === 0x1b && buffer[1] === 0x5b && buffer[2] === 0x4d) {
35
- // Mouse wheel up
36
- if (buffer[3] === 96) {
37
- if (this.#selected > 0) {
38
- this.#selected--
39
- this.#debug()
40
- }
41
- }
42
- // Mouse wheel down
43
- if (buffer[3] === 97) {
44
- if (this.#selected + 1 < this.#modules.length) {
45
- this.#selected++
46
- this.#debug()
47
- }
48
- }
49
-
50
- // Mouse click
51
- if (buffer[3] === 32) {
52
- const btn = buffer[3] - 32
53
- if (btn === 0 || btn === 1) {
54
- const x = buffer[4] - 32
55
- const y = buffer[5] - 32
56
- let c1 = (this.#width / 12) * 3
57
- if (c1 % 1 != 0) c1 = Math.floor(c1)
58
- if (c1 > 50) c1 = 50
59
- if (x > 1 && x < c1 && y < this.#height - 4) {
60
- if (this.#modules[y - 2]) {
61
- this.#selected = y - 2
62
- let index = this.#watch.indexOf(this.#selected)
63
- if (index > -1) this.#watch.splice(index, 1)
64
- else this.#watch.push(this.#selected)
65
- this.#debug()
66
- }
67
- }
68
- }
69
- }
70
- }
71
-
72
- // Ctrl+C
73
- if (buffer.length === 1 && buffer[0] === 3) {
74
- process.stdout.write('\x1b[?25h')
75
- process.stdout.write('\x1b[?1000l')
76
- process.stdout.write('\x1Bc')
77
- process.exit(0)
78
- }
79
- // Enter
80
- if (buffer.length === 1 && buffer[0] === 13) {
81
- let index = this.#watch.indexOf(this.#selected)
82
- if (index > -1) this.#watch.splice(index, 1)
83
- else this.#watch.push(this.#selected)
84
- this.#debug()
85
- }
86
- // Up/Down arrow keys
87
- if (buffer.length === 3 && buffer[0] === 27 && buffer[1] === 91) {
88
- if (buffer[2] === 65 && this.#selected > 0) this.#selected-- // up
89
- if (buffer[2] === 66 && this.#selected + 1 < this.#modules.length) this.#selected++ // down
90
- this.#debug()
91
- }
92
- process.stdout.write('\x1b[?25l')
93
- process.stdout.write('\x1b[?1000h')
94
- })
95
- }
96
-
97
- #debug() {
98
- if (this.#printing) return
99
- this.#printing = true
100
- this.#width = process.stdout.columns - 3
101
- this.#height = process.stdout.rows
102
- this.#loadModuleLogs()
103
- let c1 = (this.#width / 12) * 3
104
- if (c1 % 1 != 0) c1 = Math.floor(c1)
105
- if (c1 > 50) c1 = 50
106
- let result = ''
107
- result += Candy.cli('Cli').color('┌', 'gray')
108
- result += Candy.cli('Cli').color('─'.repeat(5), 'gray')
109
- let title = Candy.cli('Cli').color(__('Modules'), null)
110
- result += ' ' + Candy.cli('Cli').color(title) + ' '
111
- result += Candy.cli('Cli').color('─'.repeat(c1 - title.length - 7), 'gray')
112
- result += Candy.cli('Cli').color('┬', 'gray')
113
- result += Candy.cli('Cli').color('─'.repeat(5), 'gray')
114
- title = Candy.cli('Cli').color(__('Logs'), null)
115
- result += ' ' + Candy.cli('Cli').color(title) + ' '
116
- result += Candy.cli('Cli').color('─'.repeat(this.#width - c1 - title.length - 7), 'gray')
117
- result += Candy.cli('Cli').color('┐\n', 'gray')
118
- for (let i = 0; i < this.#height - 3; i++) {
119
- if (this.#modules[i]) {
120
- result += Candy.cli('Cli').color('│', 'gray')
121
- result += Candy.cli('Cli').color(
122
- '[' + (this.#watch.includes(i) ? 'X' : ' ') + '] ',
123
- i == this.#selected ? 'blue' : 'white',
124
- i == this.#selected ? 'white' : null,
125
- i == this.#selected ? 'bold' : null
126
- )
127
- result += Candy.cli('Cli').color(
128
- Candy.cli('Cli').spacing(this.#modules[i] ? this.#modules[i] : '', c1 - 4),
129
- i == this.#selected ? 'blue' : 'white',
130
- i == this.#selected ? 'white' : null,
131
- i == this.#selected ? 'bold' : null
132
- )
133
- result += Candy.cli('Cli').color('│', 'gray')
134
- } else {
135
- result += Candy.cli('Cli').color('│', 'gray')
136
- result += ' '.repeat(c1)
137
- result += Candy.cli('Cli').color('│', 'gray')
138
- }
139
- result += Candy.cli('Cli').spacing(this.#logs.content[i] ? this.#logs.content[i] : ' ', this.#width - c1)
140
- result += Candy.cli('Cli').color('│\n', 'gray')
141
- }
142
- result += Candy.cli('Cli').color('└', 'gray')
143
- result += Candy.cli('Cli').color('─'.repeat(c1), 'gray')
144
- result += Candy.cli('Cli').color('┴', 'gray')
145
- result += Candy.cli('Cli').color('─'.repeat(this.#width - c1), 'gray')
146
- result += Candy.cli('Cli').color('┘\n', 'gray')
147
- let shortcuts = '↑/↓ ' + __('Navigate') + ' | ↵ ' + __('Select') + ' | Ctrl+C ' + __('Exit')
148
- result += Candy.cli('Cli').color(' CANDYPACK', 'magenta', 'bold')
149
- result += Candy.cli('Cli').color(Candy.cli('Cli').spacing(shortcuts, this.#width + 1 - 'CANDYPACK'.length, 'right'), 'gray')
150
- if (result !== this.#current) {
151
- this.#current = result
152
- process.stdout.write('\x1Bc')
153
- process.stdout.write(result)
154
- }
155
- this.#printing = false
156
- }
157
-
158
- async #load() {
159
- if (this.#logging) return
160
- this.#logging = true
161
- this.#logs.selected = this.#selected
162
- let file = null
163
- if (this.#selected < this.#domains.length) {
164
- file = os.homedir() + '/.candypack/logs/' + this.#domains[this.#selected] + '.log'
165
- } else if (this.#selected - this.#domains.length < this.#services.length) {
166
- file = os.homedir() + '/.candypack/logs/' + this.#services[this.#selected - this.#domains.length].name + '.log'
167
- } else {
168
- this.#logging = false
169
- return
170
- }
171
- let log = ''
172
- let mtime = null
173
- if (fs.existsSync(file)) {
174
- mtime = fs.statSync(file).mtime
175
- if (this.#selected == this.#logs.selected && mtime == this.#logs.mtime) return
176
- log = fs.readFileSync(file, 'utf8')
177
- }
178
- this.#logs.content = log
179
- .trim()
180
- .replace(/\r\n/g, '\n')
181
- .split('\n')
182
- .map(line => {
183
- if ('[LOG]' == line.substring(0, 5)) {
184
- line = line.substring(5)
185
- let date = parseInt(line.substring(1, 14))
186
- line = Candy.cli('Cli').color('[' + Candy.cli('Cli').formatDate(new Date(date)) + ']', 'green', 'bold') + line.substring(15)
187
- } else if ('[ERR]' == line.substring(0, 5)) {
188
- line = line.substring(5)
189
- let date = parseInt(line.substring(1, 14))
190
- line = Candy.cli('Cli').color('[' + Candy.cli('Cli').formatDate(new Date(date)) + ']', 'red', 'bold') + line.substring(15)
191
- }
192
- return line
193
- })
194
- .slice(-this.#height + 4)
195
- this.#logs.mtime = mtime
196
- this.#logging = false
197
- }
198
-
199
- async #loadModuleLogs() {
200
- if (this.#logging) return
201
- this.#logging = true
202
-
203
- if (this.#watch.length === 0) {
204
- this.#logs.content = []
205
- this.#logging = false
206
- return
207
- }
208
-
209
- const file = os.homedir() + '/.candypack/logs/.candypack.log'
210
- let log = ''
211
- let mtime = null
212
-
213
- if (fs.existsSync(file)) {
214
- mtime = fs.statSync(file).mtime
215
- if (JSON.stringify(this.#watch) === JSON.stringify(this.#logs.watched) && mtime == this.#logs.mtime) {
216
- this.#logging = false
217
- return
218
- }
219
- log = fs.readFileSync(file, 'utf8')
220
- }
221
-
222
- const selectedModules = this.#watch.map(index => this.#modules[index])
223
- this.#logs.content = log
224
- .trim()
225
- .replace(/\r\n/g, '\n')
226
- .split('\n')
227
- .map(line => {
228
- const lowerCaseLine = line.toLowerCase()
229
- const moduleName = selectedModules.find(name => lowerCaseLine.includes(`[${name}]`.toLowerCase()))
230
- return {line, moduleName}
231
- })
232
- .filter(item => item.moduleName)
233
- .map(item => {
234
- let {line, moduleName} = item
235
- if ('[LOG]' == line.substr(0, 5) || '[ERR]' == line.substr(0, 5)) {
236
- const isError = '[ERR]' == line.substr(0, 5)
237
- const date = line.substr(6, 24)
238
- const originalMessage = line.slice(34 + moduleName.length)
239
- const cleanedMessage = originalMessage.trim()
240
- const dateColor = isError ? 'red' : 'green'
241
-
242
- line =
243
- Candy.cli('Cli').color('[' + Candy.cli('Cli').formatDate(new Date(date)) + ']', dateColor, 'bold') +
244
- Candy.cli('Cli').color(`[${moduleName}]`, 'white', 'bold') +
245
- ' ' +
246
- cleanedMessage
247
- }
248
- return line
249
- })
250
- .slice(-this.#height + 4)
251
-
252
- this.#logs.mtime = mtime
253
- this.#logs.watched = [...this.#watch]
254
- this.#logging = false
255
- }
256
-
257
- monit() {
258
- this.#monitor()
259
- setInterval(() => this.#monitor(), 250)
260
-
261
- // Mouse event handler
262
- process.stdout.write('\x1b[?25l')
263
- process.stdout.write('\x1b[?1000h')
264
- process.stdin.setRawMode(true)
265
- process.stdin.setEncoding('utf8')
266
- process.stdin.on('data', chunk => {
267
- const buffer = Buffer.from(chunk)
268
- if (buffer.length >= 6 && buffer[0] === 0x1b && buffer[1] === 0x5b && buffer[2] === 0x4d) {
269
- // Mouse wheel up
270
- if (buffer[3] === 96) {
271
- if (this.#selected > 0) {
272
- this.#selected--
273
- this.#monitor()
274
- }
275
- }
276
- // Mouse wheel down
277
- if (buffer[3] === 97) {
278
- if (this.#selected + 1 < this.#domains.length + this.#services.length) {
279
- this.#selected++
280
- this.#monitor()
281
- }
282
- }
283
-
284
- // Mouse click
285
- if (buffer[3] === 32) {
286
- const btn = buffer[3] - 32
287
- if (btn === 0 || btn === 1) {
288
- const x = buffer[4] - 32
289
- const y = buffer[5] - 32
290
- let c1 = (this.#width / 12) * 3
291
- if (c1 % 1 != 0) c1 = Math.floor(c1)
292
- if (c1 > 50) c1 = 50
293
- if (x > 1 && x < c1 && y < this.#height - 4) {
294
- if (this.#domains[y - 2]) this.#selected = y - 2
295
- else if (this.#services[y - 2 - (this.#domains.length ? 1 : 0) - this.#domains.length])
296
- this.#selected = y - 2 - (this.#domains.length ? 1 : 0)
297
- let index = this.#watch.indexOf(this.#selected)
298
- if (index > -1) this.#watch.splice(index, 1)
299
- else this.#watch.push(this.#selected)
300
- this.#monitor()
301
- }
302
- }
303
- }
304
- }
305
-
306
- // Ctrl+C
307
- if (buffer.length === 1 && buffer[0] === 3) {
308
- process.stdout.write('\x1b[?25h')
309
- process.stdout.write('\x1b[?1000l')
310
- process.stdout.write('\x1Bc')
311
- process.exit(0)
312
- }
313
- // Up/Down arrow keys
314
- if (buffer.length === 3 && buffer[0] === 27 && buffer[1] === 91) {
315
- if (buffer[2] === 65 && this.#selected > 0) this.#selected-- // up
316
- if (buffer[2] === 66 && this.#selected + 1 < this.#domains.length + this.#services.length) this.#selected++ // down
317
- this.#monitor()
318
- }
319
- process.stdout.write('\x1b[?25l')
320
- process.stdout.write('\x1b[?1000h')
321
- })
322
- }
323
-
324
- #monitor() {
325
- if (this.#printing) return
326
- this.#printing = true
327
- this.#websites = Candy.core('Config').config.websites ?? []
328
- this.#services = Candy.core('Config').config.services ?? []
329
- this.#domains = Object.keys(this.#websites)
330
- this.#width = process.stdout.columns - 3
331
- this.#height = process.stdout.rows
332
- this.#load()
333
- let c1 = (this.#width / 12) * 3
334
- if (c1 % 1 != 0) c1 = Math.floor(c1)
335
- if (c1 > 50) c1 = 50
336
- let result = ''
337
- result += Candy.cli('Cli').color('┌', 'gray')
338
- let service = -1
339
- if (this.#domains.length) {
340
- result += Candy.cli('Cli').color('─'.repeat(5), 'gray')
341
- let title = Candy.cli('Cli').color(__('Websites'), null)
342
- result += ' ' + Candy.cli('Cli').color(title) + ' '
343
- result += Candy.cli('Cli').color('─'.repeat(c1 - title.length - 7), 'gray')
344
- } else if (this.#services.length) {
345
- result += Candy.cli('Cli').color('─'.repeat(5), 'gray')
346
- let title = Candy.cli('Cli').color(__('Services'), null)
347
- result += ' ' + Candy.cli('Cli').color(title) + ' '
348
- result += Candy.cli('Cli').color('─'.repeat(c1 - title.length - 7), 'gray')
349
- service++
350
- } else {
351
- result += Candy.cli('Cli').color('─'.repeat(c1), 'gray')
352
- }
353
- result += Candy.cli('Cli').color('┬', 'gray')
354
- result += Candy.cli('Cli').color('─'.repeat(this.#width - c1), 'gray')
355
- result += Candy.cli('Cli').color('┐\n', 'gray')
356
- for (let i = 0; i < this.#height - 3; i++) {
357
- if (this.#domains[i]) {
358
- result += Candy.cli('Cli').color('│', 'gray')
359
- result += Candy.cli('Cli').icon(this.#websites[this.#domains[i]].status ?? null, i == this.#selected)
360
- result += Candy.cli('Cli').color(
361
- Candy.cli('Cli').spacing(this.#domains[i] ? this.#domains[i] : '', c1 - 3),
362
- i == this.#selected ? 'blue' : 'white',
363
- i == this.#selected ? 'white' : null,
364
- i == this.#selected ? 'bold' : null
365
- )
366
- result += Candy.cli('Cli').color('│', 'gray')
367
- } else if (this.#services.length && service == -1) {
368
- result += Candy.cli('Cli').color('├', 'gray')
369
- result += Candy.cli('Cli').color('─'.repeat(5), 'gray')
370
- let title = Candy.cli('Cli').color(__('Services'), null)
371
- result += ' ' + Candy.cli('Cli').color(title) + ' '
372
- result += Candy.cli('Cli').color('─'.repeat(c1 - title.length - 7), 'gray')
373
- result += Candy.cli('Cli').color('┤', 'gray')
374
- service++
375
- } else if (service >= 0 && service < this.#services.length) {
376
- result += Candy.cli('Cli').color('│', 'gray')
377
- result += Candy.cli('Cli').icon(this.#services[service].status ?? null, i - 1 == this.#selected)
378
- result += Candy.cli('Cli').color(
379
- Candy.cli('Cli').spacing(this.#services[service].name, c1 - 3),
380
- i - 1 == this.#selected ? 'blue' : 'white',
381
- i - 1 == this.#selected ? 'white' : null,
382
- i - 1 == this.#selected ? 'bold' : null
383
- )
384
- result += Candy.cli('Cli').color('│', 'gray')
385
- service++
386
- } else {
387
- result += Candy.cli('Cli').color('│', 'gray')
388
- result += ' '.repeat(c1)
389
- result += Candy.cli('Cli').color('│', 'gray')
390
- }
391
- if (this.#logs.selected == this.#selected) {
392
- result += Candy.cli('Cli').spacing(this.#logs.content[i] ? this.#logs.content[i] : ' ', this.#width - c1)
393
- } else {
394
- result += ' '.repeat(this.#width - c1)
395
- }
396
- result += Candy.cli('Cli').color('│\n', 'gray')
397
- }
398
- result += Candy.cli('Cli').color('└', 'gray')
399
- result += Candy.cli('Cli').color('─'.repeat(c1), 'gray')
400
- result += Candy.cli('Cli').color('┴', 'gray')
401
- result += Candy.cli('Cli').color('─'.repeat(this.#width - c1), 'gray')
402
- result += Candy.cli('Cli').color('┘\n', 'gray')
403
- let shortcuts = '↑/↓ ' + __('Navigate') + ' | Ctrl+C ' + __('Exit')
404
- result += Candy.cli('Cli').color(' CANDYPACK', 'magenta', 'bold')
405
- result += Candy.cli('Cli').color(Candy.cli('Cli').spacing(shortcuts, this.#width + 1 - 'CANDYPACK'.length, 'right'), 'gray')
406
- if (result !== this.#current) {
407
- this.#current = result
408
- process.stdout.clearLine(0)
409
- process.stdout.write('\x1Bc')
410
- process.stdout.write(result)
411
- }
412
- this.#printing = false
413
- }
414
- }
415
-
416
- module.exports = Monitor
package/core/Candy.js DELETED
@@ -1,87 +0,0 @@
1
- class CandyPack {
2
- constructor() {
3
- this._registry = new Map()
4
- this._singletons = new Map()
5
- }
6
-
7
- #instantiate(value) {
8
- if (typeof value === 'function') return new value()
9
- return value
10
- }
11
-
12
- #register(key, value, singleton = true) {
13
- this._registry.set(key, {value, singleton})
14
- }
15
-
16
- #resolve(key, requestedSingleton = null) {
17
- const entry = this._registry.get(key)
18
- if (!entry) throw new Error(`Candy: '${key}' not found`)
19
-
20
- // Use requested singleton preference if provided, otherwise use registered preference
21
- const useSingleton = requestedSingleton !== null ? requestedSingleton : entry.singleton
22
-
23
- if (useSingleton) {
24
- if (!this._singletons.has(key)) {
25
- const instance = this.#instantiate(entry.value)
26
- if (instance && typeof instance.init === 'function') {
27
- instance.init()
28
- }
29
- this._singletons.set(key, instance)
30
- }
31
- return this._singletons.get(key)
32
- }
33
-
34
- // For non-singleton, create new instance each time
35
- const instance = this.#instantiate(entry.value)
36
- if (instance && typeof instance.init === 'function') {
37
- instance.init()
38
- }
39
- return instance
40
- }
41
-
42
- core(name, singleton = true) {
43
- const key = `core:${name}`
44
- if (!this._registry.has(key)) {
45
- const modPath = `../core/${name}`
46
- let Mod = require(modPath)
47
- this.#register(key, Mod, singleton)
48
- }
49
-
50
- return this.#resolve(key, singleton)
51
- }
52
-
53
- cli(name, singleton = true) {
54
- const key = `cli:${name}`
55
- if (!this._registry.has(key)) {
56
- const modPath = `../cli/src/${name}`
57
- const Mod = require(modPath)
58
- this.#register(key, Mod, singleton)
59
- }
60
- return this.#resolve(key, singleton)
61
- }
62
-
63
- server(name, singleton = true) {
64
- const key = `server:${name}`
65
- if (!this._registry.has(key)) {
66
- const modPath = `../server/src/${name}`
67
- const Mod = require(modPath)
68
- this.#register(key, Mod, singleton)
69
- }
70
- return this.#resolve(key, singleton)
71
- }
72
-
73
- watchdog(name, singleton = true) {
74
- const key = `watchdog:${name}`
75
- if (!this._registry.has(key)) {
76
- const modPath = `../watchdog/src/${name}`
77
- const Mod = require(modPath)
78
- this.#register(key, Mod, singleton)
79
- }
80
- return this.#resolve(key, singleton)
81
- }
82
- }
83
-
84
- if (!global.Candy) {
85
- global.Candy = new CandyPack()
86
- global.__ = (...args) => Candy.core('Lang').get(...args)
87
- }