create-prisma-php-app 4.0.0-alpha.5 → 4.0.0-alpha.51

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 (53) hide show
  1. package/dist/.htaccess +54 -41
  2. package/dist/bootstrap.php +142 -97
  3. package/dist/index.js +819 -343
  4. package/dist/settings/auto-swagger-docs.ts +196 -95
  5. package/dist/settings/bs-config.ts +53 -58
  6. package/dist/settings/files-list.json +1 -1
  7. package/dist/settings/project-name.ts +2 -0
  8. package/dist/settings/restart-mcp.ts +58 -0
  9. package/dist/settings/restart-websocket.ts +51 -45
  10. package/dist/settings/utils.ts +240 -0
  11. package/dist/src/Lib/AI/ChatGPTClient.php +147 -0
  12. package/dist/src/Lib/Auth/Auth.php +544 -0
  13. package/dist/src/Lib/Auth/AuthConfig.php +89 -0
  14. package/dist/src/Lib/CacheHandler.php +121 -0
  15. package/dist/src/Lib/ErrorHandler.php +322 -0
  16. package/dist/src/Lib/FileManager/UploadFile.php +383 -0
  17. package/dist/src/Lib/Headers/Boom.php +192 -0
  18. package/dist/src/Lib/IncludeTracker.php +59 -0
  19. package/dist/src/Lib/MCP/WeatherTools.php +104 -0
  20. package/dist/src/Lib/MCP/mcp-server.php +80 -0
  21. package/dist/src/Lib/MainLayout.php +230 -0
  22. package/dist/src/Lib/Middleware/AuthMiddleware.php +157 -0
  23. package/dist/src/Lib/Middleware/CorsMiddleware.php +145 -0
  24. package/dist/src/Lib/PHPMailer/Mailer.php +169 -0
  25. package/dist/src/Lib/PartialRenderer.php +40 -0
  26. package/dist/src/Lib/PrismaPHPSettings.php +181 -0
  27. package/dist/src/Lib/Request.php +479 -0
  28. package/dist/src/Lib/Security/RateLimiter.php +33 -0
  29. package/dist/src/Lib/Set.php +102 -0
  30. package/dist/src/Lib/StateManager.php +127 -0
  31. package/dist/src/Lib/Validator.php +752 -0
  32. package/dist/src/{Websocket → Lib/Websocket}/ConnectionManager.php +1 -1
  33. package/dist/src/Lib/Websocket/websocket-server.php +118 -0
  34. package/dist/src/app/error.php +1 -1
  35. package/dist/src/app/index.php +22 -5
  36. package/dist/src/app/js/index.js +1 -1
  37. package/dist/src/app/layout.php +2 -2
  38. package/package.json +1 -1
  39. package/dist/settings/restart-websocket.bat +0 -28
  40. package/dist/src/app/assets/images/prisma-php-black.svg +0 -6
  41. package/dist/websocket-server.php +0 -22
  42. package/vendor/autoload.php +0 -25
  43. package/vendor/composer/ClassLoader.php +0 -579
  44. package/vendor/composer/InstalledVersions.php +0 -359
  45. package/vendor/composer/LICENSE +0 -21
  46. package/vendor/composer/autoload_classmap.php +0 -10
  47. package/vendor/composer/autoload_namespaces.php +0 -9
  48. package/vendor/composer/autoload_psr4.php +0 -10
  49. package/vendor/composer/autoload_real.php +0 -38
  50. package/vendor/composer/autoload_static.php +0 -25
  51. package/vendor/composer/installed.json +0 -825
  52. package/vendor/composer/installed.php +0 -132
  53. package/vendor/composer/platform_check.php +0 -26
package/dist/.htaccess CHANGED
@@ -1,38 +1,45 @@
1
1
  # Turn on rewrite engine
2
2
  RewriteEngine On
3
3
 
4
- # Prevent access to sensitive files
4
+ # ------------------------------------------------------------------------------
5
+ # Block sensitive files (Apache 2.2 + 2.4 compatible)
6
+ # ------------------------------------------------------------------------------
5
7
  <FilesMatch "(^\.htaccess|\.git|\.env|composer\.(json|lock)|package(-lock)?\.json|phpunit\.xml)$">
8
+ <IfModule mod_authz_core.c>
9
+ Require all denied
10
+ </IfModule>
11
+ <IfModule !mod_authz_core.c>
6
12
  Order allow,deny
7
13
  Deny from all
14
+ </IfModule>
8
15
  </FilesMatch>
9
16
 
10
- # Allow cross-origin requests (CORS) for all routes
11
- <IfModule mod_headers.c>
12
- Header set Access-Control-Allow-Origin "*"
13
- Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS"
14
- Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
15
- </IfModule>
17
+ # ------------------------------------------------------------------------------
18
+ # CORS: handled in PHP via Lib\Middleware\CorsMiddleware
19
+ # (Remove any Apache-level Access-Control-* headers to avoid conflicts)
20
+ # ------------------------------------------------------------------------------
16
21
 
17
- # Set Content-Type with charset UTF-8 for HTML, CSS, and JS files
22
+ # ------------------------------------------------------------------------------
23
+ # Content-Type with charset UTF-8 for HTML, CSS, and JS files
24
+ # ------------------------------------------------------------------------------
18
25
  <IfModule mod_headers.c>
19
- # For HTML files
20
- <FilesMatch "\.(html|htm)$">
21
- Header set Content-Type "text/html; charset=UTF-8"
22
- </FilesMatch>
23
-
24
- # For CSS files
25
- <FilesMatch "\.(css)$">
26
- Header set Content-Type "text/css; charset=UTF-8"
27
- </FilesMatch>
28
-
29
- # For JavaScript files
30
- <FilesMatch "\.(js)$">
31
- Header set Content-Type "application/javascript; charset=UTF-8"
32
- </FilesMatch>
26
+ # HTML
27
+ <FilesMatch "\.(html|htm)$">
28
+ Header set Content-Type "text/html; charset=UTF-8"
29
+ </FilesMatch>
30
+ # CSS
31
+ <FilesMatch "\.(css)$">
32
+ Header set Content-Type "text/css; charset=UTF-8"
33
+ </FilesMatch>
34
+ # JS
35
+ <FilesMatch "\.(js)$">
36
+ Header set Content-Type "application/javascript; charset=UTF-8"
37
+ </FilesMatch>
33
38
  </IfModule>
34
39
 
35
- # Set Content Security Policy (CSP) headers to prevent XSS attacks while allowing CDNs
40
+ # ------------------------------------------------------------------------------
41
+ # Content Security Policy
42
+ # ------------------------------------------------------------------------------
36
43
  <IfModule mod_headers.c>
37
44
  Header set Content-Security-Policy "\
38
45
  default-src 'self' https:; \
@@ -44,32 +51,38 @@ RewriteEngine On
44
51
  object-src 'none';"
45
52
  </IfModule>
46
53
 
47
- # Add important security headers
54
+ # ------------------------------------------------------------------------------
55
+ # Security headers
56
+ # ------------------------------------------------------------------------------
48
57
  <IfModule mod_headers.c>
49
- # Enforce HTTPS and prevent protocol downgrade attacks
50
- Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
58
+ # HSTS (only meaningful over HTTPS)
59
+ Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
51
60
 
52
- # Protect against Cross-Site Scripting (XSS) attacks
53
- Header set X-XSS-Protection "1; mode=block"
61
+ # XSS Protection (legacy but harmless)
62
+ Header set X-XSS-Protection "1; mode=block"
54
63
 
55
- # Prevent MIME-type sniffing
56
- Header set X-Content-Type-Options "nosniff"
64
+ # Prevent MIME-type sniffing
65
+ Header set X-Content-Type-Options "nosniff"
57
66
 
58
- # Clickjacking protection
59
- Header always set X-Frame-Options "DENY"
67
+ # Clickjacking protection
68
+ Header always set X-Frame-Options "DENY"
60
69
 
61
- # Set a strict Referrer Policy
62
- Header set Referrer-Policy "strict-origin-when-cross-origin"
70
+ # Referrer Policy
71
+ Header set Referrer-Policy "strict-origin-when-cross-origin"
63
72
 
64
- # Control browser permissions (optional but recommended)
65
- Header set Permissions-Policy "geolocation=(), microphone=(), camera=(), autoplay=()"
73
+ # Permissions Policy (tune as needed)
74
+ Header set Permissions-Policy "geolocation=(), microphone=(), camera=(), autoplay=()"
66
75
  </IfModule>
67
76
 
68
- # Exclude static files from being redirected
77
+ # ------------------------------------------------------------------------------
78
+ # Preflight: route all OPTIONS to bootstrap so CorsMiddleware can reply 204
79
+ # ------------------------------------------------------------------------------
80
+ RewriteCond %{REQUEST_METHOD} =OPTIONS
81
+ RewriteRule ^ bootstrap.php [QSA,L]
82
+
83
+ # ------------------------------------------------------------------------------
84
+ # Front controller: exclude static files, everything else -> bootstrap.php
85
+ # ------------------------------------------------------------------------------
69
86
  RewriteCond %{REQUEST_URI} !\.(css|js|png|jpe?g|gif|svg|webp|woff2?|ttf|eot|ico|pdf|mp4|webm|mp3|ogg)$ [NC]
70
87
  RewriteCond %{REQUEST_URI} !^/bootstrap.php
71
88
  RewriteRule ^(.*)$ bootstrap.php [QSA,L]
72
-
73
- # Ensure OPTIONS requests are handled correctly
74
- RewriteCond %{REQUEST_METHOD} OPTIONS
75
- RewriteRule ^ - [R=200,L]
@@ -2,26 +2,34 @@
2
2
 
3
3
  declare(strict_types=1);
4
4
 
5
- if (session_status() === PHP_SESSION_NONE) {
6
- session_start();
7
- }
8
-
9
5
  require_once __DIR__ . '/vendor/autoload.php';
10
6
  require_once __DIR__ . '/settings/paths.php';
11
7
 
12
8
  use Dotenv\Dotenv;
13
- use PPHP\Request;
14
- use PPHP\PrismaPHPSettings;
15
- use PPHP\StateManager;
16
- use PPHP\Middleware\AuthMiddleware;
17
- use PPHP\Auth\Auth;
18
- use PPHP\MainLayout;
9
+ use Lib\Middleware\CorsMiddleware;
10
+
11
+ // Load environment variables
12
+ Dotenv::createImmutable(DOCUMENT_PATH)->load();
13
+
14
+ // CORS must run before sessions/any output
15
+ CorsMiddleware::handle();
16
+
17
+ if (session_status() === PHP_SESSION_NONE) {
18
+ session_start();
19
+ }
20
+
21
+ use Lib\Request;
22
+ use Lib\PrismaPHPSettings;
23
+ use Lib\StateManager;
24
+ use Lib\Middleware\AuthMiddleware;
25
+ use Lib\Auth\Auth;
26
+ use Lib\MainLayout;
19
27
  use PPHP\PHPX\TemplateCompiler;
20
- use PPHP\CacheHandler;
21
- use PPHP\ErrorHandler;
28
+ use Lib\CacheHandler;
29
+ use Lib\ErrorHandler;
22
30
  use Firebase\JWT\JWT;
23
31
  use Firebase\JWT\Key;
24
- use PPHP\PartialRenderer;
32
+ use Lib\PartialRenderer;
25
33
 
26
34
  final class Bootstrap extends RuntimeException
27
35
  {
@@ -56,9 +64,6 @@ final class Bootstrap extends RuntimeException
56
64
 
57
65
  public static function run(): void
58
66
  {
59
- // Load environment variables
60
- Dotenv::createImmutable(DOCUMENT_PATH)->load();
61
-
62
67
  // Set timezone
63
68
  date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'UTC');
64
69
 
@@ -84,6 +89,13 @@ final class Bootstrap extends RuntimeException
84
89
  // Set a function call key as a cookie
85
90
  self::functionCallNameEncrypt();
86
91
 
92
+ self::$secondRequestC69CD = Request::$data['secondRequestC69CD'] ?? false;
93
+
94
+ // Check if the request is for a local store callback
95
+ if (Request::$isWire && !self::$secondRequestC69CD) {
96
+ self::isLocalStoreCallback();
97
+ }
98
+
87
99
  $contentInfo = self::determineContentToInclude();
88
100
  self::$contentToInclude = $contentInfo['path'] ?? '';
89
101
  self::$layoutsToInclude = $contentInfo['layouts'] ?? [];
@@ -114,7 +126,6 @@ final class Bootstrap extends RuntimeException
114
126
  self::$isContentIncluded = true;
115
127
  }
116
128
 
117
- self::$secondRequestC69CD = Request::$data['secondRequestC69CD'] ?? false;
118
129
  self::$isPartialRequest =
119
130
  !empty(Request::$data['pphpSync71163'])
120
131
  && !empty(Request::$data['selectors'])
@@ -128,6 +139,31 @@ final class Bootstrap extends RuntimeException
128
139
  ErrorHandler::checkFatalError();
129
140
  }
130
141
 
142
+ private static function isLocalStoreCallback(): void
143
+ {
144
+ $data = self::getRequestData();
145
+
146
+ if (empty($data['callback'])) {
147
+ self::jsonExit(['success' => false, 'error' => 'Callback not provided', 'response' => null]);
148
+ }
149
+
150
+ try {
151
+ $aesKey = self::getAesKeyFromJwt();
152
+ } catch (RuntimeException $e) {
153
+ self::jsonExit(['success' => false, 'error' => $e->getMessage()]);
154
+ }
155
+
156
+ try {
157
+ $callbackName = self::decryptCallback($data['callback'], $aesKey);
158
+ } catch (RuntimeException $e) {
159
+ self::jsonExit(['success' => false, 'error' => $e->getMessage()]);
160
+ }
161
+
162
+ if ($callbackName === PrismaPHPSettings::$localStoreKey) {
163
+ self::jsonExit(['success' => true, 'response' => 'localStorage updated']);
164
+ }
165
+ }
166
+
131
167
  private static function functionCallNameEncrypt(): void
132
168
  {
133
169
  $hmacSecret = $_ENV['FUNCTION_CALL_SECRET'] ?? '';
@@ -588,108 +624,117 @@ final class Bootstrap extends RuntimeException
588
624
 
589
625
  public static function wireCallback(): void
590
626
  {
591
- $response = [
592
- 'success' => false,
593
- 'error' => 'Callback not provided',
594
- 'response' => null,
595
- ];
627
+ $data = self::getRequestData();
596
628
 
597
- if (!empty($_FILES)) {
598
- $data = $_POST;
599
- foreach ($_FILES as $key => $file) {
600
- if (is_array($file['name'])) {
601
- $files = [];
602
- foreach ($file['name'] as $i => $name) {
603
- $files[] = [
604
- 'name' => $name,
605
- 'type' => $file['type'][$i],
606
- 'tmp_name' => $file['tmp_name'][$i],
607
- 'error' => $file['error'][$i],
608
- 'size' => $file['size'][$i],
609
- ];
610
- }
611
- $data[$key] = $files;
612
- } else {
613
- $data[$key] = $file;
614
- }
615
- }
616
- } else {
617
- $raw = file_get_contents('php://input');
618
- $data = json_decode($raw, true);
619
- if (json_last_error() !== JSON_ERROR_NONE) {
620
- $data = $_POST;
621
- }
629
+ if (empty($data['callback'])) {
630
+ self::jsonExit(['success' => false, 'error' => 'Callback not provided', 'response' => null]);
622
631
  }
623
632
 
624
- if (empty($data['callback'])) {
625
- echo json_encode($response, JSON_UNESCAPED_UNICODE);
626
- exit;
633
+ try {
634
+ $aesKey = self::getAesKeyFromJwt();
635
+ } catch (RuntimeException $e) {
636
+ self::jsonExit(['success' => false, 'error' => $e->getMessage()]);
637
+ }
638
+
639
+ try {
640
+ $callbackName = self::decryptCallback($data['callback'], $aesKey);
641
+ } catch (RuntimeException $e) {
642
+ self::jsonExit(['success' => false, 'error' => $e->getMessage()]);
627
643
  }
628
644
 
645
+ $args = self::convertToArrayObject($data);
646
+ $out = str_contains($callbackName, '->') || str_contains($callbackName, '::')
647
+ ? self::dispatchMethod($callbackName, $args)
648
+ : self::dispatchFunction($callbackName, $args);
649
+
650
+ if ($out !== null) {
651
+ self::jsonExit($out);
652
+ }
653
+ exit;
654
+ }
655
+
656
+ private static function getAesKeyFromJwt(): string
657
+ {
629
658
  $token = $_COOKIE['pphp_function_call_jwt'] ?? null;
630
659
  $jwtSecret = $_ENV['FUNCTION_CALL_SECRET'] ?? null;
660
+
631
661
  if (!$token || !$jwtSecret) {
632
- echo json_encode(['success' => false, 'error' => 'Missing session key or secret'], JSON_UNESCAPED_UNICODE);
633
- exit;
662
+ throw new RuntimeException('Missing session key or secret');
634
663
  }
664
+
635
665
  try {
636
666
  $decoded = JWT::decode($token, new Key($jwtSecret, 'HS256'));
637
667
  } catch (Throwable) {
638
- echo json_encode(['success' => false, 'error' => 'Invalid session key'], JSON_UNESCAPED_UNICODE);
639
- exit;
668
+ throw new RuntimeException('Invalid session key');
640
669
  }
670
+
641
671
  $aesKey = base64_decode($decoded->k, true);
642
672
  if ($aesKey === false || strlen($aesKey) !== 32) {
643
- echo json_encode(['success' => false, 'error' => 'Bad key length'], JSON_UNESCAPED_UNICODE);
644
- exit;
673
+ throw new RuntimeException('Bad key length');
645
674
  }
646
675
 
647
- $parts = explode(':', $data['callback'], 2);
676
+ return $aesKey;
677
+ }
678
+
679
+ private static function jsonExit(array $payload): void
680
+ {
681
+ echo json_encode($payload, JSON_UNESCAPED_UNICODE);
682
+ exit;
683
+ }
684
+
685
+ private static function decryptCallback(string $encrypted, string $aesKey): string
686
+ {
687
+ $parts = explode(':', $encrypted, 2);
648
688
  if (count($parts) !== 2) {
649
- echo json_encode(['success' => false, 'error' => 'Malformed callback payload'], JSON_UNESCAPED_UNICODE);
650
- exit;
689
+ throw new RuntimeException('Malformed callback payload');
651
690
  }
652
- [$iv_b64, $ct_b64] = $parts;
653
- $iv = base64_decode($iv_b64, true);
654
- $ct = base64_decode($ct_b64, true);
691
+ [$ivB64, $ctB64] = $parts;
692
+
693
+ $iv = base64_decode($ivB64, true);
694
+ $ct = base64_decode($ctB64, true);
695
+
655
696
  if ($iv === false || strlen($iv) !== 16 || $ct === false) {
656
- echo json_encode(['success' => false, 'error' => 'Invalid callback payload'], JSON_UNESCAPED_UNICODE);
657
- exit;
697
+ throw new RuntimeException('Invalid callback payload');
658
698
  }
699
+
659
700
  $plain = openssl_decrypt($ct, 'AES-256-CBC', $aesKey, OPENSSL_RAW_DATA, $iv);
660
701
  if ($plain === false) {
661
- echo json_encode(['success' => false, 'error' => 'Decryption failed'], JSON_UNESCAPED_UNICODE);
662
- exit;
702
+ throw new RuntimeException('Decryption failed');
663
703
  }
664
704
 
665
- $callbackName = preg_replace('/[^a-zA-Z0-9_:\->]/', '', $plain);
666
- if ($callbackName === '' || $callbackName[0] === '_') {
667
- echo json_encode(['success' => false, 'error' => 'Invalid callback'], JSON_UNESCAPED_UNICODE);
668
- exit;
705
+ $callback = preg_replace('/[^a-zA-Z0-9_:\->]/', '', $plain);
706
+ if ($callback === '' || $callback[0] === '_') {
707
+ throw new RuntimeException('Invalid callback');
669
708
  }
670
709
 
671
- if (
672
- isset($data['callback']) &&
673
- $callbackName === PrismaPHPSettings::$localStoreKey
674
- ) {
675
- echo json_encode([
676
- 'success' => true,
677
- 'response' => 'localStorage updated',
678
- ], JSON_UNESCAPED_UNICODE);
679
- exit;
680
- }
710
+ return $callback;
711
+ }
681
712
 
682
- $args = self::convertToArrayObject($data);
683
- if (strpos($callbackName, '->') !== false || strpos($callbackName, '::') !== false) {
684
- $out = self::dispatchMethod($callbackName, $args);
685
- } else {
686
- $out = self::dispatchFunction($callbackName, $args);
713
+ private static function getRequestData(): array
714
+ {
715
+ if (!empty($_FILES)) {
716
+ $data = $_POST;
717
+ foreach ($_FILES as $key => $file) {
718
+ $data[$key] = is_array($file['name'])
719
+ ? array_map(
720
+ fn($i) => [
721
+ 'name' => $file['name'][$i],
722
+ 'type' => $file['type'][$i],
723
+ 'tmp_name' => $file['tmp_name'][$i],
724
+ 'error' => $file['error'][$i],
725
+ 'size' => $file['size'][$i],
726
+ ],
727
+ array_keys($file['name'])
728
+ )
729
+ : $file;
730
+ }
731
+ return $data;
687
732
  }
688
733
 
689
- if ($out !== null) {
690
- echo json_encode($out, JSON_UNESCAPED_UNICODE);
691
- }
692
- exit;
734
+ $raw = file_get_contents('php://input');
735
+ $json = json_decode($raw, true);
736
+
737
+ return (json_last_error() === JSON_ERROR_NONE) ? $json : $_POST;
693
738
  }
694
739
 
695
740
  private static function dispatchFunction(string $fn, mixed $args)
@@ -993,13 +1038,6 @@ try {
993
1038
  Bootstrap::createUpdateRequestData();
994
1039
  }
995
1040
 
996
- // If there’s caching
997
- if (isset(Bootstrap::$requestFilesData[Request::$decodedUri])) {
998
- if ($_ENV['CACHE_ENABLED'] === 'true') {
999
- CacheHandler::serveCache(Request::$decodedUri, intval($_ENV['CACHE_TTL']));
1000
- }
1001
- }
1002
-
1003
1041
  // For wire calls, re-include the files if needed
1004
1042
  if (Request::$isWire && !Bootstrap::$secondRequestC69CD) {
1005
1043
  if (isset(Bootstrap::$requestFilesData[Request::$decodedUri])) {
@@ -1019,6 +1057,13 @@ try {
1019
1057
  Bootstrap::wireCallback();
1020
1058
  }
1021
1059
 
1060
+ // If there’s caching
1061
+ if ((!Request::$isWire && !Bootstrap::$secondRequestC69CD) && isset(Bootstrap::$requestFilesData[Request::$decodedUri])) {
1062
+ if ($_ENV['CACHE_ENABLED'] === 'true') {
1063
+ CacheHandler::serveCache(Request::$decodedUri, intval($_ENV['CACHE_TTL']));
1064
+ }
1065
+ }
1066
+
1022
1067
  MainLayout::$children = MainLayout::$childLayoutChildren . Bootstrap::getLoadingsFiles();
1023
1068
 
1024
1069
  ob_start();
@@ -1029,7 +1074,7 @@ try {
1029
1074
  MainLayout::$html = "<!DOCTYPE html>\n" . MainLayout::$html;
1030
1075
 
1031
1076
  if (
1032
- http_response_code() === 200 && isset(Bootstrap::$requestFilesData[Request::$decodedUri]['fileName']) && $_ENV['CACHE_ENABLED'] === 'true'
1077
+ http_response_code() === 200 && isset(Bootstrap::$requestFilesData[Request::$decodedUri]['fileName']) && $_ENV['CACHE_ENABLED'] === 'true' && (!Request::$isWire && !Bootstrap::$secondRequestC69CD)
1033
1078
  ) {
1034
1079
  CacheHandler::saveCache(Request::$decodedUri, MainLayout::$html);
1035
1080
  }