nibula 1.6.0 → 1.6.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.
Files changed (99) hide show
  1. package/.eleventy.js +81 -81
  2. package/CHANGELOG.md +384 -377
  3. package/LICENSE +169 -169
  4. package/NOTICE +4 -4
  5. package/README.md +110 -110
  6. package/bin/create.js +505 -505
  7. package/bin/delegate.js +57 -57
  8. package/bin/nibula.js +302 -302
  9. package/docs/Assistant CLI.md +65 -65
  10. package/docs/Backend.md +201 -201
  11. package/docs/Components.md +140 -140
  12. package/docs/Creating pages.md +69 -69
  13. package/docs/Deploy.md +210 -210
  14. package/docs/Head and SEO.md +139 -139
  15. package/docs/Javascript.md +126 -126
  16. package/docs/Styling with SCSS.md +139 -139
  17. package/nginx.conf +113 -113
  18. package/package.json +69 -69
  19. package/src/backend/.htaccess +6 -6
  20. package/src/backend/_core/composer.json +5 -5
  21. package/src/backend/_core/composer.lock +492 -492
  22. package/src/backend/_core/index.js +267 -267
  23. package/src/backend/_core/index.php +160 -160
  24. package/src/backend/_core/init.js +52 -52
  25. package/src/backend/_core/init.php +33 -33
  26. package/src/backend/_core/modules/RateLimiter.js +58 -58
  27. package/src/backend/_core/modules/RateLimiter.php +30 -30
  28. package/src/backend/_core/modules/Response.js +59 -59
  29. package/src/backend/_core/modules/Response.php +48 -48
  30. package/src/backend/api/protected/example-protected.js +23 -23
  31. package/src/backend/api/protected/example-protected.php +16 -16
  32. package/src/backend/api/public/example-public.js +24 -24
  33. package/src/backend/api/public/example-public.php +16 -16
  34. package/src/backend/database/Database.js +46 -46
  35. package/src/backend/database/Database.php +23 -23
  36. package/src/backend/example.config.js +37 -37
  37. package/src/backend/example.config.php +37 -37
  38. package/src/backend/package.json +17 -17
  39. package/src/backend/web.config +43 -43
  40. package/src/frontend/.htaccess +51 -51
  41. package/src/frontend/404.njk +17 -17
  42. package/src/frontend/assets/brand/favicon.svg +54 -54
  43. package/src/frontend/assets/brand/logo.svg +54 -54
  44. package/src/frontend/components/global/footer.njk +25 -25
  45. package/src/frontend/components/global/header.njk +6 -6
  46. package/src/frontend/components/welcome.njk +115 -115
  47. package/src/frontend/index.njk +8 -8
  48. package/src/frontend/js/global.js +10 -10
  49. package/src/frontend/js/modules/exampleModule.js +2 -2
  50. package/src/frontend/js/pages/404.js +8 -8
  51. package/src/frontend/js/pages/homepage.js +8 -8
  52. package/src/frontend/layouts/base.njk +136 -136
  53. package/src/frontend/layouts/page-components.njk +13 -13
  54. package/src/frontend/llms.njk +17 -17
  55. package/src/frontend/robots.njk +7 -7
  56. package/src/frontend/scss/_global.scss +46 -46
  57. package/src/frontend/scss/_root.scss +13 -13
  58. package/src/frontend/scss/modules/_animations.scss +24 -24
  59. package/src/frontend/scss/modules/_footer.scss +28 -28
  60. package/src/frontend/scss/modules/_header.scss +26 -26
  61. package/src/frontend/scss/modules/_mobile.scss +29 -29
  62. package/src/frontend/scss/modules/_typography.scss +18 -18
  63. package/src/frontend/scss/modules/frameworks/_bootstrap.scss +109 -109
  64. package/src/frontend/scss/modules/frameworks/_bulma.scss +108 -108
  65. package/src/frontend/scss/modules/frameworks/_foundation.scss +137 -137
  66. package/src/frontend/scss/modules/frameworks/_uikit.scss +109 -109
  67. package/src/frontend/scss/pages/404.scss +27 -27
  68. package/src/frontend/scss/pages/homepage.scss +22 -22
  69. package/src/frontend/sitemap.njk +17 -17
  70. package/src/frontend/ts/global.ts +11 -11
  71. package/src/frontend/ts/modules/exampleModule.ts +2 -2
  72. package/src/frontend/ts/pages/404.ts +8 -8
  73. package/src/frontend/ts/pages/homepage.ts +8 -8
  74. package/src/frontend/web.config +51 -51
  75. package/tools/assistant.js +122 -122
  76. package/tools/buildJs.js +39 -39
  77. package/tools/cleanOutput.js +23 -23
  78. package/tools/cli/prompt.js +40 -40
  79. package/tools/cli/ui.js +87 -87
  80. package/tools/config/messages.json +80 -80
  81. package/tools/config/settings.json +130 -130
  82. package/tools/lib/colors.js +17 -17
  83. package/tools/lib/files.js +75 -75
  84. package/tools/lib/logger.js +22 -22
  85. package/tools/lib/outputPath.js +141 -141
  86. package/tools/lib/pageActions.js +146 -146
  87. package/tools/lib/pageArtifacts.js +46 -46
  88. package/tools/lib/pageComponents.js +88 -88
  89. package/tools/lib/pagesData.js +87 -87
  90. package/tools/lib/paths.js +63 -63
  91. package/tools/lib/project.js +54 -54
  92. package/tools/lib/text.js +47 -47
  93. package/tools/lib/validation.js +40 -40
  94. package/tools/res/templates/template.js +7 -7
  95. package/tools/res/templates/template.njk +8 -8
  96. package/tools/res/templates/template.scss +22 -22
  97. package/tools/res/templates/template.ts +8 -8
  98. package/tsconfig.json +24 -24
  99. package/src/frontend/scss/pages/prova.scss +0 -23
@@ -1,31 +1,31 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- class RateLimiter {
6
- public static function check(string $ip, int $maxRequests = 60, int $windowSeconds = 60): void {
7
- $cacheDir = __DIR__ . '/../../cache';
8
-
9
- if (!is_dir($cacheDir)) {
10
- mkdir($cacheDir, 0755, true);
11
- }
12
-
13
- $cacheFile = $cacheDir . '/rl_' . md5($ip) . '.json';
14
- $now = time();
15
- $data = [];
16
-
17
- if (file_exists($cacheFile)) {
18
- $data = json_decode(file_get_contents($cacheFile), true) ?: [];
19
- }
20
-
21
- $data = array_filter($data, fn($ts) => $ts > ($now - $windowSeconds));
22
- $data[] = $now;
23
-
24
- file_put_contents($cacheFile, json_encode(array_values($data)), LOCK_EX);
25
-
26
- if (count($data) > $maxRequests) {
27
- header('Retry-After: ' . $windowSeconds);
28
- Response::error('Too Many Requests', 429);
29
- }
30
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ class RateLimiter {
6
+ public static function check(string $ip, int $maxRequests = 60, int $windowSeconds = 60): void {
7
+ $cacheDir = __DIR__ . '/../../cache';
8
+
9
+ if (!is_dir($cacheDir)) {
10
+ mkdir($cacheDir, 0755, true);
11
+ }
12
+
13
+ $cacheFile = $cacheDir . '/rl_' . md5($ip) . '.json';
14
+ $now = time();
15
+ $data = [];
16
+
17
+ if (file_exists($cacheFile)) {
18
+ $data = json_decode(file_get_contents($cacheFile), true) ?: [];
19
+ }
20
+
21
+ $data = array_filter($data, fn($ts) => $ts > ($now - $windowSeconds));
22
+ $data[] = $now;
23
+
24
+ file_put_contents($cacheFile, json_encode(array_values($data)), LOCK_EX);
25
+
26
+ if (count($data) > $maxRequests) {
27
+ header('Retry-After: ' . $windowSeconds);
28
+ Response::error('Too Many Requests', 429);
29
+ }
30
+ }
31
31
  }
@@ -1,59 +1,59 @@
1
- 'use strict';
2
-
3
- /**
4
- * Mirror of src/backend/_core/modules/Response.php
5
- *
6
- * In PHP the Response helper writes JSON and calls exit() to stop execution.
7
- * Node has no per-request exit, so each method writes to the response and then
8
- * throws the HALT sentinel. The front controller catches HALT and stops the
9
- * dispatch, reproducing the same "the endpoint stops here" behaviour.
10
- *
11
- * A fresh, response-bound helper is created per request via createResponse(res)
12
- * so overlapping async requests never clash (PHP was process-per-request).
13
- */
14
-
15
- // Sentinel thrown to unwind execution after a response has been sent (≈ PHP exit).
16
- const HALT = Symbol('RESPONSE_HALT');
17
-
18
- function jsonEncode(obj) {
19
- // JSON.stringify already leaves unicode unescaped and does not escape slashes,
20
- // matching PHP's JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES.
21
- return JSON.stringify(obj);
22
- }
23
-
24
- function createResponse(res) {
25
- return {
26
- success(data = null, code = 200) {
27
- res.statusCode = code;
28
- res.setHeader('Content-Type', 'application/json; charset=UTF-8');
29
- res.end(jsonEncode({
30
- status: 'success',
31
- data: data,
32
- }));
33
- throw HALT;
34
- },
35
-
36
- error(message, code = 400, details = null) {
37
- res.statusCode = code;
38
- res.setHeader('Content-Type', 'application/json; charset=UTF-8');
39
- const body = {
40
- status: 'error',
41
- message: message,
42
- code: code,
43
- };
44
- if (details !== null) {
45
- body.details = details;
46
- }
47
- res.end(jsonEncode(body));
48
- throw HALT;
49
- },
50
-
51
- noContent() {
52
- res.statusCode = 204;
53
- res.end();
54
- throw HALT;
55
- },
56
- };
57
- }
58
-
59
- module.exports = { createResponse, HALT };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Mirror of src/backend/_core/modules/Response.php
5
+ *
6
+ * In PHP the Response helper writes JSON and calls exit() to stop execution.
7
+ * Node has no per-request exit, so each method writes to the response and then
8
+ * throws the HALT sentinel. The front controller catches HALT and stops the
9
+ * dispatch, reproducing the same "the endpoint stops here" behaviour.
10
+ *
11
+ * A fresh, response-bound helper is created per request via createResponse(res)
12
+ * so overlapping async requests never clash (PHP was process-per-request).
13
+ */
14
+
15
+ // Sentinel thrown to unwind execution after a response has been sent (≈ PHP exit).
16
+ const HALT = Symbol('RESPONSE_HALT');
17
+
18
+ function jsonEncode(obj) {
19
+ // JSON.stringify already leaves unicode unescaped and does not escape slashes,
20
+ // matching PHP's JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES.
21
+ return JSON.stringify(obj);
22
+ }
23
+
24
+ function createResponse(res) {
25
+ return {
26
+ success(data = null, code = 200) {
27
+ res.statusCode = code;
28
+ res.setHeader('Content-Type', 'application/json; charset=UTF-8');
29
+ res.end(jsonEncode({
30
+ status: 'success',
31
+ data: data,
32
+ }));
33
+ throw HALT;
34
+ },
35
+
36
+ error(message, code = 400, details = null) {
37
+ res.statusCode = code;
38
+ res.setHeader('Content-Type', 'application/json; charset=UTF-8');
39
+ const body = {
40
+ status: 'error',
41
+ message: message,
42
+ code: code,
43
+ };
44
+ if (details !== null) {
45
+ body.details = details;
46
+ }
47
+ res.end(jsonEncode(body));
48
+ throw HALT;
49
+ },
50
+
51
+ noContent() {
52
+ res.statusCode = 204;
53
+ res.end();
54
+ throw HALT;
55
+ },
56
+ };
57
+ }
58
+
59
+ module.exports = { createResponse, HALT };
@@ -1,49 +1,49 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- if (!defined('CORE_ACCESS')) {
6
- $errorPage = $_SERVER['DOCUMENT_ROOT'] . '/404.html';
7
- http_response_code(404);
8
- if (file_exists($errorPage)) {
9
- header('Content-Type: text/html; charset=UTF-8');
10
- echo file_get_contents($errorPage);
11
- } else {
12
- echo "404 Not Found";
13
- }
14
- exit;
15
- }
16
-
17
- class Response
18
- {
19
- public static function success(mixed $data = null, int $code = 200): never
20
- {
21
- http_response_code($code);
22
- echo json_encode([
23
- 'status' => 'success',
24
- 'data' => $data,
25
- ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
26
- exit;
27
- }
28
-
29
- public static function error(string $message, int $code = 400, mixed $details = null): never
30
- {
31
- http_response_code($code);
32
- $body = [
33
- 'status' => 'error',
34
- 'message' => $message,
35
- 'code' => $code,
36
- ];
37
- if ($details !== null) {
38
- $body['details'] = $details;
39
- }
40
- echo json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
41
- exit;
42
- }
43
-
44
- public static function noContent(): never
45
- {
46
- http_response_code(204);
47
- exit;
48
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ if (!defined('CORE_ACCESS')) {
6
+ $errorPage = $_SERVER['DOCUMENT_ROOT'] . '/404.html';
7
+ http_response_code(404);
8
+ if (file_exists($errorPage)) {
9
+ header('Content-Type: text/html; charset=UTF-8');
10
+ echo file_get_contents($errorPage);
11
+ } else {
12
+ echo "404 Not Found";
13
+ }
14
+ exit;
15
+ }
16
+
17
+ class Response
18
+ {
19
+ public static function success(mixed $data = null, int $code = 200): never
20
+ {
21
+ http_response_code($code);
22
+ echo json_encode([
23
+ 'status' => 'success',
24
+ 'data' => $data,
25
+ ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
26
+ exit;
27
+ }
28
+
29
+ public static function error(string $message, int $code = 400, mixed $details = null): never
30
+ {
31
+ http_response_code($code);
32
+ $body = [
33
+ 'status' => 'error',
34
+ 'message' => $message,
35
+ 'code' => $code,
36
+ ];
37
+ if ($details !== null) {
38
+ $body['details'] = $details;
39
+ }
40
+ echo json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
41
+ exit;
42
+ }
43
+
44
+ public static function noContent(): never
45
+ {
46
+ http_response_code(204);
47
+ exit;
48
+ }
49
49
  }
@@ -1,23 +1,23 @@
1
- 'use strict';
2
-
3
- /**
4
- * Mirror of src/backend/api/protected/example-protected.php
5
- *
6
- * Protected endpoint: the API key check (X-Api-Key header) runs automatically in
7
- * the front controller before this file is loaded, so here we just handle logic.
8
- */
9
-
10
- module.exports = ({ method, requestParams, Response }) => {
11
- if (method !== 'GET') {
12
- Response.error('Method not allowed', 405);
13
- }
14
-
15
- //
16
- // Your endpoint logic here. You can access route parameters in requestParams
17
- //
18
-
19
- Response.success({
20
- message: 'Protected endpoint is working',
21
- params: requestParams,
22
- });
23
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Mirror of src/backend/api/protected/example-protected.php
5
+ *
6
+ * Protected endpoint: the API key check (X-Api-Key header) runs automatically in
7
+ * the front controller before this file is loaded, so here we just handle logic.
8
+ */
9
+
10
+ module.exports = ({ method, requestParams, Response }) => {
11
+ if (method !== 'GET') {
12
+ Response.error('Method not allowed', 405);
13
+ }
14
+
15
+ //
16
+ // Your endpoint logic here. You can access route parameters in requestParams
17
+ //
18
+
19
+ Response.success({
20
+ message: 'Protected endpoint is working',
21
+ params: requestParams,
22
+ });
23
+ };
@@ -1,17 +1,17 @@
1
- <?php
2
- declare(strict_types=1);
3
-
4
- require_once CORE_PATH . '/modules/Response.php';
5
-
6
- if ($method !== 'GET') {
7
- Response::error('Method not allowed', 405);
8
- }
9
-
10
- //
11
- // Your endpoint logic here. You can access route parameters in $requestParams array
12
- //
13
-
14
- Response::success([
15
- 'message' => 'Protected endpoint is working',
16
- 'params' => $requestParams,
1
+ <?php
2
+ declare(strict_types=1);
3
+
4
+ require_once CORE_PATH . '/modules/Response.php';
5
+
6
+ if ($method !== 'GET') {
7
+ Response::error('Method not allowed', 405);
8
+ }
9
+
10
+ //
11
+ // Your endpoint logic here. You can access route parameters in $requestParams array
12
+ //
13
+
14
+ Response::success([
15
+ 'message' => 'Protected endpoint is working',
16
+ 'params' => $requestParams,
17
17
  ]);
@@ -1,24 +1,24 @@
1
- 'use strict';
2
-
3
- /**
4
- * Mirror of src/backend/api/public/example-public.php
5
- *
6
- * Public endpoint: no API key required. Receives the request context and calls
7
- * the request-bound Response helper, exactly like the PHP file used the global
8
- * Response class and $method / $requestParams.
9
- */
10
-
11
- module.exports = ({ method, requestParams, Response }) => {
12
- if (method !== 'GET') {
13
- Response.error('Method not allowed', 405);
14
- }
15
-
16
- //
17
- // Your endpoint logic here. You can access route parameters in requestParams
18
- //
19
-
20
- Response.success({
21
- message: 'Public endpoint is working',
22
- params: requestParams,
23
- });
24
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Mirror of src/backend/api/public/example-public.php
5
+ *
6
+ * Public endpoint: no API key required. Receives the request context and calls
7
+ * the request-bound Response helper, exactly like the PHP file used the global
8
+ * Response class and $method / $requestParams.
9
+ */
10
+
11
+ module.exports = ({ method, requestParams, Response }) => {
12
+ if (method !== 'GET') {
13
+ Response.error('Method not allowed', 405);
14
+ }
15
+
16
+ //
17
+ // Your endpoint logic here. You can access route parameters in requestParams
18
+ //
19
+
20
+ Response.success({
21
+ message: 'Public endpoint is working',
22
+ params: requestParams,
23
+ });
24
+ };
@@ -1,17 +1,17 @@
1
- <?php
2
- declare(strict_types=1);
3
-
4
- require_once CORE_PATH . '/modules/Response.php';
5
-
6
- if ($method !== 'GET') {
7
- Response::error('Method not allowed', 405);
8
- }
9
-
10
- //
11
- // Your endpoint logic here. You can access route parameters in $requestParams array
12
- //
13
-
14
- Response::success([
15
- 'message' => 'Public endpoint is working',
16
- 'params' => $requestParams,
1
+ <?php
2
+ declare(strict_types=1);
3
+
4
+ require_once CORE_PATH . '/modules/Response.php';
5
+
6
+ if ($method !== 'GET') {
7
+ Response::error('Method not allowed', 405);
8
+ }
9
+
10
+ //
11
+ // Your endpoint logic here. You can access route parameters in $requestParams array
12
+ //
13
+
14
+ Response::success([
15
+ 'message' => 'Public endpoint is working',
16
+ 'params' => $requestParams,
17
17
  ]);
@@ -1,46 +1,46 @@
1
- 'use strict';
2
-
3
- /**
4
- * Mirror of src/backend/database/Database.php
5
- *
6
- * PDO singleton → a single shared mysql2 connection pool. Config is read from
7
- * ../config.js (same relative location as the PHP version's ../config.php).
8
- * Options map the PDO attributes as closely as mysql2 allows:
9
- * - ERRMODE_EXCEPTION → mysql2 rejects/throws on error by default
10
- * - FETCH_ASSOC → rowsAsArray:false (objects keyed by column) is default
11
- * - EMULATE_PREPARES false → native prepared statements via pool.execute()
12
- *
13
- * Requires the "mysql2" package (npm install mysql2). getInstance() returns the
14
- * promise-based pool; use pool.execute(sql, params) for prepared statements.
15
- */
16
-
17
- const path = require('path');
18
-
19
- let instance = null;
20
-
21
- class Database {
22
- static getInstance() {
23
- if (instance === null) {
24
- // Lazy require so the backend boots even without a DB configured.
25
- // eslint-disable-next-line global-require
26
- const mysql = require('mysql2/promise');
27
-
28
- // eslint-disable-next-line global-require
29
- const config = require(path.join(__dirname, '..', 'config.js'));
30
-
31
- instance = mysql.createPool({
32
- host: config.DB_HOST,
33
- database: config.DB_NAME,
34
- user: config.DB_USER,
35
- password: config.DB_PASS,
36
- charset: 'utf8mb4',
37
- waitForConnections: true,
38
- connectionLimit: 10,
39
- namedPlaceholders: false,
40
- });
41
- }
42
- return instance;
43
- }
44
- }
45
-
46
- module.exports = Database;
1
+ 'use strict';
2
+
3
+ /**
4
+ * Mirror of src/backend/database/Database.php
5
+ *
6
+ * PDO singleton → a single shared mysql2 connection pool. Config is read from
7
+ * ../config.js (same relative location as the PHP version's ../config.php).
8
+ * Options map the PDO attributes as closely as mysql2 allows:
9
+ * - ERRMODE_EXCEPTION → mysql2 rejects/throws on error by default
10
+ * - FETCH_ASSOC → rowsAsArray:false (objects keyed by column) is default
11
+ * - EMULATE_PREPARES false → native prepared statements via pool.execute()
12
+ *
13
+ * Requires the "mysql2" package (npm install mysql2). getInstance() returns the
14
+ * promise-based pool; use pool.execute(sql, params) for prepared statements.
15
+ */
16
+
17
+ const path = require('path');
18
+
19
+ let instance = null;
20
+
21
+ class Database {
22
+ static getInstance() {
23
+ if (instance === null) {
24
+ // Lazy require so the backend boots even without a DB configured.
25
+ // eslint-disable-next-line global-require
26
+ const mysql = require('mysql2/promise');
27
+
28
+ // eslint-disable-next-line global-require
29
+ const config = require(path.join(__dirname, '..', 'config.js'));
30
+
31
+ instance = mysql.createPool({
32
+ host: config.DB_HOST,
33
+ database: config.DB_NAME,
34
+ user: config.DB_USER,
35
+ password: config.DB_PASS,
36
+ charset: 'utf8mb4',
37
+ waitForConnections: true,
38
+ connectionLimit: 10,
39
+ namedPlaceholders: false,
40
+ });
41
+ }
42
+ return instance;
43
+ }
44
+ }
45
+
46
+ module.exports = Database;
@@ -1,24 +1,24 @@
1
- <?php
2
- declare(strict_types=1);
3
-
4
- class Database {
5
- private static ?PDO $instance = null;
6
-
7
- private function __construct() {}
8
-
9
- public static function getInstance(): PDO {
10
- if (self::$instance === null) {
11
- $config = require __DIR__ . '/../config.php';
12
-
13
- $dsn = "mysql:host={$config['DB_HOST']};dbname={$config['DB_NAME']};charset=utf8mb4";
14
- $options = [
15
- PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
16
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
17
- PDO::ATTR_EMULATE_PREPARES => false,
18
- ];
19
-
20
- self::$instance = new PDO($dsn, $config['DB_USER'], $config['DB_PASS'], $options);
21
- }
22
- return self::$instance;
23
- }
1
+ <?php
2
+ declare(strict_types=1);
3
+
4
+ class Database {
5
+ private static ?PDO $instance = null;
6
+
7
+ private function __construct() {}
8
+
9
+ public static function getInstance(): PDO {
10
+ if (self::$instance === null) {
11
+ $config = require __DIR__ . '/../config.php';
12
+
13
+ $dsn = "mysql:host={$config['DB_HOST']};dbname={$config['DB_NAME']};charset=utf8mb4";
14
+ $options = [
15
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
16
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
17
+ PDO::ATTR_EMULATE_PREPARES => false,
18
+ ];
19
+
20
+ self::$instance = new PDO($dsn, $config['DB_USER'], $config['DB_PASS'], $options);
21
+ }
22
+ return self::$instance;
23
+ }
24
24
  }
@@ -1,37 +1,37 @@
1
- 'use strict';
2
-
3
- /**
4
- * Mirror of src/backend/example.config.php
5
- *
6
- * Versioned, secret-free template. On setup, copy this file to config.js and
7
- * fill in real values. config.js is git-ignored and stays local.
8
- */
9
-
10
- module.exports = {
11
- // Default key for protected endpoints that don't have a specific key in CUSTOM_ENDPOINT_KEYS
12
- GENERAL_API_KEY: 'DEFAULT_KEY',
13
-
14
- // If you want to restrict access to protected endpoints to specific clients, define custom keys per endpoint.
15
- // For subfolder endpoints, use the relative path ('subfolder/endpoint')
16
- CUSTOM_ENDPOINT_KEYS: {
17
- 'subfolder/example-protected': 'custom-key',
18
- },
19
-
20
- GENERAL_ALLOWED_ORIGINS: [
21
- '*',
22
- // 'https://example.com',
23
- ],
24
-
25
- CUSTOM_ENDPOINT_ORIGINS: {
26
- 'subfolder/example-protected': ['https://app.example.com'],
27
- },
28
-
29
- // Database configuration
30
- DB_HOST: '127.0.0.1',
31
- DB_NAME: 'example_db',
32
- DB_USER: 'root',
33
- DB_PASS: '',
34
-
35
- // Environment: 'production' hides error details; anything else = debug.
36
- APP_ENV: 'production',
37
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Mirror of src/backend/example.config.php
5
+ *
6
+ * Versioned, secret-free template. On setup, copy this file to config.js and
7
+ * fill in real values. config.js is git-ignored and stays local.
8
+ */
9
+
10
+ module.exports = {
11
+ // Default key for protected endpoints that don't have a specific key in CUSTOM_ENDPOINT_KEYS
12
+ GENERAL_API_KEY: 'DEFAULT_KEY',
13
+
14
+ // If you want to restrict access to protected endpoints to specific clients, define custom keys per endpoint.
15
+ // For subfolder endpoints, use the relative path ('subfolder/endpoint')
16
+ CUSTOM_ENDPOINT_KEYS: {
17
+ 'subfolder/example-protected': 'custom-key',
18
+ },
19
+
20
+ GENERAL_ALLOWED_ORIGINS: [
21
+ '*',
22
+ // 'https://example.com',
23
+ ],
24
+
25
+ CUSTOM_ENDPOINT_ORIGINS: {
26
+ 'subfolder/example-protected': ['https://app.example.com'],
27
+ },
28
+
29
+ // Database configuration
30
+ DB_HOST: '127.0.0.1',
31
+ DB_NAME: 'example_db',
32
+ DB_USER: 'root',
33
+ DB_PASS: '',
34
+
35
+ // Environment: 'production' hides error details; anything else = debug.
36
+ APP_ENV: 'production',
37
+ };