odac 0.9.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 (213) hide show
  1. package/.editorconfig +21 -0
  2. package/.github/workflows/auto-pr-description.yml +49 -0
  3. package/.github/workflows/release.yml +32 -0
  4. package/.github/workflows/test-coverage.yml +58 -0
  5. package/.husky/pre-commit +2 -0
  6. package/.kiro/steering/code-style.md +56 -0
  7. package/.kiro/steering/product.md +20 -0
  8. package/.kiro/steering/structure.md +77 -0
  9. package/.kiro/steering/tech.md +87 -0
  10. package/.prettierrc +10 -0
  11. package/.releaserc.js +134 -0
  12. package/AGENTS.md +84 -0
  13. package/CHANGELOG.md +181 -0
  14. package/CODE_OF_CONDUCT.md +83 -0
  15. package/CONTRIBUTING.md +63 -0
  16. package/LICENSE +661 -0
  17. package/README.md +57 -0
  18. package/SECURITY.md +26 -0
  19. package/bin/candy +10 -0
  20. package/bin/candypack +10 -0
  21. package/cli/index.js +3 -0
  22. package/cli/src/Cli.js +348 -0
  23. package/cli/src/Connector.js +93 -0
  24. package/cli/src/Monitor.js +416 -0
  25. package/core/Candy.js +87 -0
  26. package/core/Commands.js +239 -0
  27. package/core/Config.js +1094 -0
  28. package/core/Lang.js +52 -0
  29. package/core/Log.js +43 -0
  30. package/core/Process.js +26 -0
  31. package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
  32. package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
  33. package/docs/backend/01-overview/03-development-server.md +79 -0
  34. package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
  35. package/docs/backend/03-config/00-configuration-overview.md +214 -0
  36. package/docs/backend/03-config/01-database-connection.md +60 -0
  37. package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
  38. package/docs/backend/03-config/03-request-timeout.md +11 -0
  39. package/docs/backend/03-config/04-environment-variables.md +227 -0
  40. package/docs/backend/03-config/05-early-hints.md +352 -0
  41. package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
  42. package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
  43. package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
  44. package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
  45. package/docs/backend/04-routing/05-advanced-routing.md +14 -0
  46. package/docs/backend/04-routing/06-error-pages.md +101 -0
  47. package/docs/backend/04-routing/07-cron-jobs.md +149 -0
  48. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
  49. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
  50. package/docs/backend/05-controllers/03-controller-classes.md +93 -0
  51. package/docs/backend/05-forms/01-custom-forms.md +395 -0
  52. package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
  53. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
  54. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
  55. package/docs/backend/07-views/01-the-view-directory.md +73 -0
  56. package/docs/backend/07-views/02-rendering-a-view.md +179 -0
  57. package/docs/backend/07-views/03-template-syntax.md +181 -0
  58. package/docs/backend/07-views/03-variables.md +328 -0
  59. package/docs/backend/07-views/04-request-data.md +231 -0
  60. package/docs/backend/07-views/05-conditionals.md +290 -0
  61. package/docs/backend/07-views/06-loops.md +353 -0
  62. package/docs/backend/07-views/07-translations.md +358 -0
  63. package/docs/backend/07-views/08-backend-javascript.md +398 -0
  64. package/docs/backend/07-views/09-comments.md +297 -0
  65. package/docs/backend/08-database/01-database-connection.md +99 -0
  66. package/docs/backend/08-database/02-using-mysql.md +322 -0
  67. package/docs/backend/09-validation/01-the-validator-service.md +424 -0
  68. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
  69. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
  70. package/docs/backend/10-authentication/03-register.md +134 -0
  71. package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
  72. package/docs/backend/10-authentication/05-session-management.md +159 -0
  73. package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
  74. package/docs/backend/11-mail/01-the-mail-service.md +42 -0
  75. package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
  76. package/docs/backend/13-utilities/01-candy-var.md +504 -0
  77. package/docs/frontend/01-overview/01-introduction.md +146 -0
  78. package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
  79. package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
  80. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
  81. package/docs/frontend/03-forms/01-form-handling.md +420 -0
  82. package/docs/frontend/04-api-requests/01-get-post.md +443 -0
  83. package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
  84. package/docs/index.json +452 -0
  85. package/docs/server/01-installation/01-quick-install.md +19 -0
  86. package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
  87. package/docs/server/02-get-started/01-core-concepts.md +7 -0
  88. package/docs/server/02-get-started/02-basic-commands.md +57 -0
  89. package/docs/server/02-get-started/03-cli-reference.md +276 -0
  90. package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
  91. package/docs/server/03-service/01-start-a-new-service.md +57 -0
  92. package/docs/server/03-service/02-delete-a-service.md +48 -0
  93. package/docs/server/04-web/01-create-a-website.md +36 -0
  94. package/docs/server/04-web/02-list-websites.md +9 -0
  95. package/docs/server/04-web/03-delete-a-website.md +29 -0
  96. package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
  97. package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
  98. package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
  99. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
  100. package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
  101. package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
  102. package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
  103. package/docs/server/07-mail/04-change-account-password.md +23 -0
  104. package/eslint.config.mjs +120 -0
  105. package/framework/index.js +4 -0
  106. package/framework/src/Auth.js +309 -0
  107. package/framework/src/Candy.js +81 -0
  108. package/framework/src/Config.js +79 -0
  109. package/framework/src/Env.js +60 -0
  110. package/framework/src/Lang.js +57 -0
  111. package/framework/src/Mail.js +83 -0
  112. package/framework/src/Mysql.js +575 -0
  113. package/framework/src/Request.js +301 -0
  114. package/framework/src/Route/Cron.js +128 -0
  115. package/framework/src/Route/Internal.js +439 -0
  116. package/framework/src/Route.js +455 -0
  117. package/framework/src/Server.js +15 -0
  118. package/framework/src/Stream.js +163 -0
  119. package/framework/src/Token.js +37 -0
  120. package/framework/src/Validator.js +271 -0
  121. package/framework/src/Var.js +211 -0
  122. package/framework/src/View/EarlyHints.js +190 -0
  123. package/framework/src/View/Form.js +600 -0
  124. package/framework/src/View.js +513 -0
  125. package/framework/web/candy.js +838 -0
  126. package/jest.config.js +22 -0
  127. package/locale/de-DE.json +80 -0
  128. package/locale/en-US.json +79 -0
  129. package/locale/es-ES.json +80 -0
  130. package/locale/fr-FR.json +80 -0
  131. package/locale/pt-BR.json +80 -0
  132. package/locale/ru-RU.json +80 -0
  133. package/locale/tr-TR.json +85 -0
  134. package/locale/zh-CN.json +80 -0
  135. package/package.json +86 -0
  136. package/server/index.js +5 -0
  137. package/server/src/Api.js +88 -0
  138. package/server/src/DNS.js +940 -0
  139. package/server/src/Hub.js +535 -0
  140. package/server/src/Mail.js +571 -0
  141. package/server/src/SSL.js +180 -0
  142. package/server/src/Server.js +27 -0
  143. package/server/src/Service.js +248 -0
  144. package/server/src/Subdomain.js +64 -0
  145. package/server/src/Web/Firewall.js +170 -0
  146. package/server/src/Web/Proxy.js +134 -0
  147. package/server/src/Web.js +451 -0
  148. package/server/src/mail/imap.js +1091 -0
  149. package/server/src/mail/server.js +32 -0
  150. package/server/src/mail/smtp.js +786 -0
  151. package/test/cli/Cli.test.js +36 -0
  152. package/test/core/Candy.test.js +234 -0
  153. package/test/core/Commands.test.js +538 -0
  154. package/test/core/Config.test.js +1435 -0
  155. package/test/core/Lang.test.js +250 -0
  156. package/test/core/Process.test.js +156 -0
  157. package/test/framework/Route.test.js +239 -0
  158. package/test/framework/View/EarlyHints.test.js +282 -0
  159. package/test/scripts/check-coverage.js +132 -0
  160. package/test/server/Api.test.js +647 -0
  161. package/test/server/Client.test.js +338 -0
  162. package/test/server/DNS.test.js +2050 -0
  163. package/test/server/DNS.test.js.bak +2084 -0
  164. package/test/server/Log.test.js +73 -0
  165. package/test/server/Mail.account.test_.js +460 -0
  166. package/test/server/Mail.init.test_.js +411 -0
  167. package/test/server/Mail.test_.js +1340 -0
  168. package/test/server/SSL.test_.js +1491 -0
  169. package/test/server/Server.test.js +765 -0
  170. package/test/server/Service.test_.js +1127 -0
  171. package/test/server/Subdomain.test.js +440 -0
  172. package/test/server/Web/Firewall.test.js +175 -0
  173. package/test/server/Web.test_.js +1562 -0
  174. package/test/server/__mocks__/acme-client.js +17 -0
  175. package/test/server/__mocks__/bcrypt.js +50 -0
  176. package/test/server/__mocks__/child_process.js +389 -0
  177. package/test/server/__mocks__/crypto.js +432 -0
  178. package/test/server/__mocks__/fs.js +450 -0
  179. package/test/server/__mocks__/globalCandy.js +227 -0
  180. package/test/server/__mocks__/http-proxy.js +105 -0
  181. package/test/server/__mocks__/http.js +575 -0
  182. package/test/server/__mocks__/https.js +272 -0
  183. package/test/server/__mocks__/index.js +249 -0
  184. package/test/server/__mocks__/mail/server.js +100 -0
  185. package/test/server/__mocks__/mail/smtp.js +31 -0
  186. package/test/server/__mocks__/mailparser.js +81 -0
  187. package/test/server/__mocks__/net.js +369 -0
  188. package/test/server/__mocks__/node-forge.js +328 -0
  189. package/test/server/__mocks__/os.js +320 -0
  190. package/test/server/__mocks__/path.js +291 -0
  191. package/test/server/__mocks__/selfsigned.js +8 -0
  192. package/test/server/__mocks__/server/src/mail/server.js +100 -0
  193. package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
  194. package/test/server/__mocks__/smtp-server.js +106 -0
  195. package/test/server/__mocks__/sqlite3.js +394 -0
  196. package/test/server/__mocks__/testFactories.js +299 -0
  197. package/test/server/__mocks__/testHelpers.js +363 -0
  198. package/test/server/__mocks__/tls.js +229 -0
  199. package/watchdog/index.js +3 -0
  200. package/watchdog/src/Watchdog.js +156 -0
  201. package/web/config.json +5 -0
  202. package/web/controller/page/about.js +27 -0
  203. package/web/controller/page/index.js +34 -0
  204. package/web/package.json +18 -0
  205. package/web/public/assets/css/style.css +1835 -0
  206. package/web/public/assets/js/app.js +96 -0
  207. package/web/route/www.js +19 -0
  208. package/web/skeleton/main.html +22 -0
  209. package/web/view/content/about.html +65 -0
  210. package/web/view/content/home.html +205 -0
  211. package/web/view/footer/main.html +11 -0
  212. package/web/view/head/main.html +5 -0
  213. package/web/view/header/main.html +14 -0
@@ -0,0 +1,156 @@
1
+ const {spawn} = require('child_process')
2
+ const fs = require('fs').promises
3
+ const os = require('os')
4
+ const path = require('path')
5
+
6
+ // --- Constants ---
7
+ const CANDYPACK_HOME = path.join(os.homedir(), '.candypack')
8
+ const LOG_DIR = path.join(CANDYPACK_HOME, 'logs')
9
+ const SERVER_SCRIPT_PATH = path.join(__dirname, '..', '..', 'server', 'index.js')
10
+
11
+ const MAX_RESTARTS_IN_WINDOW = 100
12
+ const RESTART_WINDOW_MS = 1000 * 60 * 5 // 5 minutes
13
+ const SAVE_INTERVAL_MS = 1000 // 1 second
14
+
15
+ class Watchdog {
16
+ #logBuffer = ''
17
+ #errorBuffer = ''
18
+ #restartCount = 0
19
+ #lastRestartTimestamp = 0
20
+ #isSaving = false
21
+
22
+ init() {
23
+ // Set up periodic log saving. This is done only once.
24
+ setInterval(() => this.#saveLogs(), SAVE_INTERVAL_MS)
25
+
26
+ // Start the server for the first time.
27
+ this.#startServer()
28
+ }
29
+
30
+ /**
31
+ * Saves the buffered logs to their respective files.
32
+ * This function is designed to be called periodically.
33
+ * Keeps only the last 1000 lines in each log file.
34
+ */
35
+ async #saveLogs() {
36
+ if (this.#isSaving) return
37
+ this.#isSaving = true
38
+
39
+ try {
40
+ // Ensure log directory exists before attempting to write files
41
+ await fs.mkdir(LOG_DIR, {recursive: true})
42
+ const logFile = path.join(LOG_DIR, '.candypack.log')
43
+ const errFile = path.join(LOG_DIR, '.candypack_err.log')
44
+
45
+ // Limit log buffer to last 1000 lines
46
+ const logLines = this.#logBuffer.split('\n')
47
+ if (logLines.length > 1000) {
48
+ this.#logBuffer = logLines.slice(-1000).join('\n')
49
+ }
50
+
51
+ // Limit error buffer to last 1000 lines
52
+ const errLines = this.#errorBuffer.split('\n')
53
+ if (errLines.length > 1000) {
54
+ this.#errorBuffer = errLines.slice(-1000).join('\n')
55
+ }
56
+
57
+ await fs.writeFile(logFile, this.#logBuffer, 'utf8')
58
+ await fs.writeFile(errFile, this.#errorBuffer, 'utf8')
59
+ } catch (error) {
60
+ console.error('Failed to save logs:', error)
61
+ } finally {
62
+ this.#isSaving = false
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Performs startup checks to ensure a clean environment.
68
+ * It kills any old watchdog or server processes that might still be running.
69
+ * It also creates the necessary configuration files and directories if they don't exist.
70
+ * @returns {Promise<boolean>} A promise that resolves to true if the checks pass.
71
+ */
72
+ async #performStartupChecks() {
73
+ try {
74
+ // Kill previous watchdog process if it exists and is different from the current one
75
+ if (Candy.core('Config').config.server.watchdog && Candy.core('Config').config.server.watchdog !== process.pid)
76
+ await Candy.core('Process').stop(Candy.core('Config').config.server.watchdog)
77
+
78
+ // Kill previous server process if it exists
79
+ if (Candy.core('Config').config.server.pid) await Candy.core('Process').stop(Candy.core('Config').config.server.pid)
80
+
81
+ for (const domain of Object.keys(Candy.core('Config').config?.websites ?? []))
82
+ if (Candy.core('Config').config.websites[domain].pid)
83
+ await Candy.core('Process').stop(Candy.core('Config').config.websites[domain].pid)
84
+
85
+ for (const service of Candy.core('Config').config.services ?? []) if (service.pid) await Candy.core('Process').stop(service.pid)
86
+
87
+ // Update config with current watchdog's info
88
+ Candy.core('Config').config.server.watchdog = process.pid
89
+ Candy.core('Config').config.server.started = Date.now()
90
+ Candy.core('Config').force()
91
+
92
+ return new Promise(resolve => setTimeout(() => resolve(true), 1000))
93
+ } catch (error) {
94
+ console.error('Error during startup checks:', error)
95
+ return false
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Starts the server process and sets up monitoring.
101
+ */
102
+ async #startServer() {
103
+ const checksPassed = await this.#performStartupChecks()
104
+ if (!checksPassed) {
105
+ console.error('Startup checks failed. Aborting.')
106
+ process.exit(1)
107
+ }
108
+
109
+ // Ensure log directory exists before starting
110
+ await fs.mkdir(LOG_DIR, {recursive: true})
111
+
112
+ const child = spawn('node', [SERVER_SCRIPT_PATH])
113
+
114
+ process.on('exit', () => child.kill())
115
+
116
+ Candy.core('Config').config.server.pid = child.pid
117
+
118
+ console.log(`Watchdog process started with PID: ${process.pid}`)
119
+ console.log(`Server process started with PID: ${child.pid}`)
120
+
121
+ child.stdout.on('data', data => {
122
+ this.#logBuffer += `[LOG][${new Date().toISOString()}] ${data.toString()}`
123
+ })
124
+
125
+ child.stderr.on('data', data => {
126
+ this.#logBuffer += `[ERR][${new Date().toISOString()}] ${data.toString()}`
127
+ this.#errorBuffer += `[ERR][${new Date().toISOString()}] ${data.toString()}`
128
+ })
129
+
130
+ child.on('close', code => {
131
+ Candy.core('Config').reload()
132
+ this.#errorBuffer += `[ERR][${new Date().toISOString()}] Process closed with code ${code}\n`
133
+
134
+ // Reset restart count if the last restart was a while ago
135
+ if (Date.now() - this.#lastRestartTimestamp > RESTART_WINDOW_MS) {
136
+ this.#restartCount = 0
137
+ }
138
+
139
+ this.#restartCount++
140
+ this.#lastRestartTimestamp = Date.now()
141
+
142
+ // If restart limit is not exceeded, restart the server
143
+ if (this.#restartCount < MAX_RESTARTS_IN_WINDOW) {
144
+ console.log('Server process closed. Restarting...')
145
+ // Relaunch the server process without setting up new intervals
146
+ this.#startServer()
147
+ } else {
148
+ console.error('Server has crashed too many times. Not restarting.')
149
+ // Final attempt to save logs before exiting
150
+ this.#saveLogs().then(() => process.exit(1))
151
+ }
152
+ })
153
+ }
154
+ }
155
+
156
+ module.exports = new Watchdog()
@@ -0,0 +1,5 @@
1
+ {
2
+ "route": {
3
+ "/assets/js/candy.js": "${candy}/framework/web/candy.js"
4
+ }
5
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * About Page Controller
3
+ *
4
+ * This controller renders the about page using CandyPack's skeleton-based view system.
5
+ * Provides information about CandyPack and its key components.
6
+ *
7
+ * For AJAX requests, only content is returned. For full page loads, skeleton + content.
8
+ */
9
+
10
+ module.exports = function (Candy) {
11
+ // Set variables for AJAX responses
12
+ Candy.set(
13
+ {
14
+ pageTitle: 'About CandyPack',
15
+ version: '1.0.0'
16
+ },
17
+ true
18
+ )
19
+
20
+ Candy.View.set({
21
+ skeleton: 'main',
22
+ head: 'main',
23
+ header: 'main',
24
+ content: 'about',
25
+ footer: 'main'
26
+ })
27
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Home Page Controller
3
+ *
4
+ * This controller renders the home page using CandyPack's skeleton-based view system.
5
+ * The skeleton provides the layout (header, nav, footer) and the view provides the content.
6
+ *
7
+ * For AJAX requests (candy-link navigation), only the content is returned.
8
+ * For full page loads, skeleton + content is returned.
9
+ *
10
+ * This page demonstrates:
11
+ * - Modern, responsive design
12
+ * - candy.js AJAX form handling
13
+ * - candy.js GET requests
14
+ * - Dynamic page loading with candy-link
15
+ */
16
+
17
+ module.exports = function (Candy) {
18
+ // Set variables that will be available in AJAX responses
19
+ Candy.set(
20
+ {
21
+ welcomeMessage: 'Welcome to CandyPack!',
22
+ timestamp: Date.now()
23
+ },
24
+ true
25
+ ) // true = include in AJAX responses
26
+
27
+ Candy.View.set({
28
+ skeleton: 'main',
29
+ head: 'main',
30
+ header: 'main',
31
+ content: 'home',
32
+ footer: 'main'
33
+ })
34
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "{{domain}}",
3
+ "version": "1.0.0",
4
+ "description": "Website for {{domain_original}}",
5
+ "scripts": {
6
+ "start": "candy framework run",
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "dependencies": {
10
+ "candypack": "*"
11
+ },
12
+ "keywords": [
13
+ "candypack",
14
+ "website"
15
+ ],
16
+ "author": "",
17
+ "license": "ISC"
18
+ }