create-prisma-php-app 4.0.0-alpha.2 → 4.0.0-alpha.21

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 (59) hide show
  1. package/dist/.htaccess +54 -41
  2. package/dist/bootstrap.php +143 -98
  3. package/dist/index.js +264 -99
  4. package/dist/settings/auto-swagger-docs.ts +196 -95
  5. package/dist/settings/bs-config.ts +56 -58
  6. package/dist/settings/files-list.json +1 -1
  7. package/dist/settings/restart-mcp.ts +58 -0
  8. package/dist/settings/restart-websocket.ts +51 -45
  9. package/dist/settings/utils.ts +240 -0
  10. package/dist/src/Lib/AI/ChatGPTClient.php +147 -0
  11. package/dist/src/Lib/Auth/Auth.php +544 -0
  12. package/dist/src/Lib/Auth/AuthConfig.php +89 -0
  13. package/dist/src/Lib/CacheHandler.php +121 -0
  14. package/dist/src/Lib/ErrorHandler.php +322 -0
  15. package/dist/src/Lib/FileManager/UploadFile.php +383 -0
  16. package/dist/src/Lib/Headers/Boom.php +192 -0
  17. package/dist/src/Lib/IncludeTracker.php +59 -0
  18. package/dist/src/Lib/MCP/WeatherTools.php +104 -0
  19. package/dist/src/Lib/MCP/mcp-server.php +80 -0
  20. package/dist/src/Lib/MainLayout.php +230 -0
  21. package/dist/src/Lib/Middleware/AuthMiddleware.php +154 -0
  22. package/dist/src/Lib/Middleware/CorsMiddleware.php +145 -0
  23. package/dist/src/Lib/PHPMailer/Mailer.php +169 -0
  24. package/dist/src/Lib/PHPX/Exceptions/ComponentValidationException.php +49 -0
  25. package/dist/src/Lib/PHPX/Fragment.php +32 -0
  26. package/dist/src/Lib/PHPX/IPHPX.php +22 -0
  27. package/dist/src/Lib/PHPX/PHPX.php +287 -0
  28. package/dist/src/Lib/PHPX/TemplateCompiler.php +641 -0
  29. package/dist/src/Lib/PHPX/TwMerge.php +346 -0
  30. package/dist/src/Lib/PHPX/TypeCoercer.php +490 -0
  31. package/dist/src/Lib/PartialRenderer.php +40 -0
  32. package/dist/src/Lib/PrismaPHPSettings.php +181 -0
  33. package/dist/src/Lib/Request.php +479 -0
  34. package/dist/src/Lib/Security/RateLimiter.php +33 -0
  35. package/dist/src/Lib/Set.php +102 -0
  36. package/dist/src/Lib/StateManager.php +127 -0
  37. package/dist/src/Lib/Validator.php +752 -0
  38. package/dist/src/{Websocket → Lib/Websocket}/ConnectionManager.php +1 -1
  39. package/dist/src/Lib/Websocket/websocket-server.php +118 -0
  40. package/dist/src/app/error.php +1 -1
  41. package/dist/src/app/index.php +24 -5
  42. package/dist/src/app/js/index.js +1 -1
  43. package/dist/src/app/layout.php +2 -2
  44. package/package.json +1 -1
  45. package/dist/settings/restart-websocket.bat +0 -28
  46. package/dist/src/app/assets/images/prisma-php-black.svg +0 -6
  47. package/dist/websocket-server.php +0 -22
  48. package/vendor/autoload.php +0 -25
  49. package/vendor/composer/ClassLoader.php +0 -579
  50. package/vendor/composer/InstalledVersions.php +0 -359
  51. package/vendor/composer/LICENSE +0 -21
  52. package/vendor/composer/autoload_classmap.php +0 -10
  53. package/vendor/composer/autoload_namespaces.php +0 -9
  54. package/vendor/composer/autoload_psr4.php +0 -10
  55. package/vendor/composer/autoload_real.php +0 -38
  56. package/vendor/composer/autoload_static.php +0 -25
  57. package/vendor/composer/installed.json +0 -825
  58. package/vendor/composer/installed.php +0 -132
  59. package/vendor/composer/platform_check.php +0 -26
@@ -0,0 +1,145 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib\Middleware;
6
+
7
+ final class CorsMiddleware
8
+ {
9
+ /** Entry point */
10
+ public static function handle(?array $overrides = null): void
11
+ {
12
+ // Not a CORS request
13
+ $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
14
+ if ($origin === '') {
15
+ return;
16
+ }
17
+
18
+ // Resolve config (env → overrides)
19
+ $cfg = self::buildConfig($overrides);
20
+
21
+ // Not allowed? Do nothing (browser will block)
22
+ if (!self::isAllowedOrigin($origin, $cfg['allowedOrigins'])) {
23
+ return;
24
+ }
25
+
26
+ // Compute which value to send for Access-Control-Allow-Origin
27
+ // If credentials are disabled and '*' is in list, we can send '*'
28
+ $sendWildcard = (!$cfg['allowCredentials'] && self::listHasWildcard($cfg['allowedOrigins']));
29
+ $allowOriginValue = $sendWildcard ? '*' : self::normalize($origin);
30
+
31
+ // Vary for caches
32
+ header('Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers');
33
+
34
+ header('Access-Control-Allow-Origin: ' . $allowOriginValue);
35
+ if ($cfg['allowCredentials']) {
36
+ header('Access-Control-Allow-Credentials: true');
37
+ }
38
+
39
+ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
40
+ // Preflight response
41
+ $requestedHeaders = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'] ?? '';
42
+ $allowedHeaders = $cfg['allowedHeaders'] !== ''
43
+ ? $cfg['allowedHeaders']
44
+ : ($requestedHeaders ?: 'Content-Type, Authorization, X-Requested-With');
45
+
46
+ header('Access-Control-Allow-Methods: ' . $cfg['allowedMethods']);
47
+ header('Access-Control-Allow-Headers: ' . $allowedHeaders);
48
+ if ($cfg['maxAge'] > 0) {
49
+ header('Access-Control-Max-Age: ' . (string) $cfg['maxAge']);
50
+ }
51
+
52
+ // Optional: Private Network Access preflights (Chrome)
53
+ if (!empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK'])) {
54
+ header('Access-Control-Allow-Private-Network: true');
55
+ }
56
+
57
+ http_response_code(204);
58
+ header('Content-Length: 0');
59
+ exit;
60
+ }
61
+
62
+ // Simple/actual request
63
+ if ($cfg['exposeHeaders'] !== '') {
64
+ header('Access-Control-Expose-Headers: ' . $cfg['exposeHeaders']);
65
+ }
66
+ }
67
+
68
+ /** Read env + normalize + apply overrides */
69
+ private static function buildConfig(?array $overrides): array
70
+ {
71
+ $allowed = self::parseList($_ENV['CORS_ALLOWED_ORIGINS'] ?? '');
72
+ $cfg = [
73
+ 'allowedOrigins' => $allowed,
74
+ 'allowCredentials' => filter_var($_ENV['CORS_ALLOW_CREDENTIALS'] ?? 'false', FILTER_VALIDATE_BOOLEAN),
75
+ 'allowedMethods' => $_ENV['CORS_ALLOWED_METHODS'] ?? 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
76
+ 'allowedHeaders' => trim($_ENV['CORS_ALLOWED_HEADERS'] ?? ''),
77
+ 'exposeHeaders' => trim($_ENV['CORS_EXPOSE_HEADERS'] ?? ''),
78
+ 'maxAge' => (int)($_ENV['CORS_MAX_AGE'] ?? 86400),
79
+ ];
80
+
81
+ if (is_array($overrides)) {
82
+ foreach ($overrides as $k => $v) {
83
+ if (array_key_exists($k, $cfg)) {
84
+ $cfg[$k] = $v;
85
+ }
86
+ }
87
+ }
88
+
89
+ // Normalize patterns
90
+ $cfg['allowedOrigins'] = array_map([self::class, 'normalize'], $cfg['allowedOrigins']);
91
+ return $cfg;
92
+ }
93
+
94
+ /** CSV or JSON array → array<string> */
95
+ private static function parseList(string $raw): array
96
+ {
97
+ $raw = trim($raw);
98
+ if ($raw === '') return [];
99
+
100
+ if ($raw[0] === '[') {
101
+ $arr = json_decode($raw, true);
102
+ if (is_array($arr)) {
103
+ return array_values(array_filter(array_map('strval', $arr), 'strlen'));
104
+ }
105
+ }
106
+ return array_values(array_filter(array_map('trim', explode(',', $raw)), 'strlen'));
107
+ }
108
+
109
+ private static function normalize(string $origin): string
110
+ {
111
+ return rtrim($origin, '/');
112
+ }
113
+
114
+ private static function isAllowedOrigin(string $origin, array $list): bool
115
+ {
116
+ $o = self::normalize($origin);
117
+
118
+ foreach ($list as $pattern) {
119
+ $p = self::normalize($pattern);
120
+
121
+ // literal "*"
122
+ if ($p === '*') return true;
123
+
124
+ // allow literal "null" for file:// or sandboxed if explicitly listed
125
+ if ($o === 'null' && strtolower($p) === 'null') return true;
126
+
127
+ // wildcard like https://*.example.com
128
+ if (strpos($p, '*') !== false) {
129
+ $regex = '/^' . str_replace('\*', '[^.]+', preg_quote($p, '/')) . '$/i';
130
+ if (preg_match($regex, $o)) return true;
131
+ } else {
132
+ if (strcasecmp($p, $o) === 0) return true;
133
+ }
134
+ }
135
+ return false;
136
+ }
137
+
138
+ private static function listHasWildcard(array $list): bool
139
+ {
140
+ foreach ($list as $p) {
141
+ if (trim($p) === '*') return true;
142
+ }
143
+ return false;
144
+ }
145
+ }
@@ -0,0 +1,169 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib\PHPMailer;
6
+
7
+ use PHPMailer\PHPMailer\PHPMailer;
8
+ use PHPMailer\PHPMailer\Exception;
9
+ use Lib\Validator;
10
+
11
+ class Mailer
12
+ {
13
+ private PHPMailer $mail;
14
+
15
+ public function __construct()
16
+ {
17
+ $this->mail = new PHPMailer(true);
18
+ $this->mail->CharSet = 'UTF-8';
19
+ $this->setup();
20
+ }
21
+
22
+ private function setup(): void
23
+ {
24
+ $this->mail->isSMTP();
25
+ $this->mail->SMTPDebug = 0;
26
+ $this->mail->Host = $_ENV['SMTP_HOST'];
27
+ $this->mail->SMTPAuth = true;
28
+ $this->mail->Username = $_ENV['SMTP_USERNAME'];
29
+ $this->mail->Password = $_ENV['SMTP_PASSWORD'];
30
+ $this->mail->SMTPSecure = $_ENV['SMTP_ENCRYPTION'];
31
+ $this->mail->Port = (int) $_ENV['SMTP_PORT'];
32
+ $this->mail->setFrom($_ENV['MAIL_FROM'], $_ENV['MAIL_FROM_NAME']);
33
+ }
34
+
35
+ /**
36
+ * Send an email.
37
+ *
38
+ * @param string $to The recipient's email address.
39
+ * @param string $subject The subject of the email.
40
+ * @param string $body The HTML body of the email.
41
+ * @param array $options (optional) Additional email options like name, altBody, CC, BCC, and attachments.
42
+ * - attachments: A string or an array of file paths, or an array of associative arrays with keys 'path' and 'name'.
43
+ *
44
+ * @return bool Returns true if the email is sent successfully, false otherwise.
45
+ *
46
+ * @throws Exception Throws an exception if the email could not be sent.
47
+ */
48
+ public function send(string $to, string $subject, string $body, array $options = []): bool
49
+ {
50
+ try {
51
+ // Validate and sanitize inputs
52
+ $to = Validator::email($to);
53
+ if (!$to) {
54
+ throw new Exception('Invalid email address for the main recipient');
55
+ }
56
+
57
+ $subject = Validator::string($subject);
58
+ $body = Validator::html($body);
59
+ $altBody = $this->convertToPlainText($body);
60
+
61
+ $name = $options['name'] ?? '';
62
+ $addCC = $options['addCC'] ?? [];
63
+ $addBCC = $options['addBCC'] ?? [];
64
+ $attachments = $options['attachments'] ?? [];
65
+
66
+ $name = Validator::string($name);
67
+
68
+ // Handle CC recipients
69
+ $this->handleRecipients($addCC, 'CC');
70
+ // Handle BCC recipients
71
+ $this->handleRecipients($addBCC, 'BCC');
72
+ // Handle file attachments if provided
73
+ if (!empty($attachments)) {
74
+ $this->handleAttachments($attachments);
75
+ }
76
+
77
+ // Set the main recipient and other email properties
78
+ $this->mail->addAddress($to, $name);
79
+ $this->mail->isHTML(true);
80
+ $this->mail->Subject = $subject;
81
+ $this->mail->Body = $body;
82
+ $this->mail->AltBody = $altBody;
83
+
84
+ // Send the email
85
+ return $this->mail->send();
86
+ } catch (Exception $e) {
87
+ throw new Exception($e->getMessage());
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Handle adding CC or BCC recipients.
93
+ *
94
+ * @param string|array $recipients Email addresses to add.
95
+ * @param string $type Type of recipient ('CC' or 'BCC').
96
+ *
97
+ * @throws Exception Throws an exception if any email address is invalid.
98
+ */
99
+ private function handleRecipients(string|array $recipients, string $type): void
100
+ {
101
+ if (!empty($recipients)) {
102
+ $method = $type === 'CC' ? 'addCC' : 'addBCC';
103
+
104
+ if (is_array($recipients)) {
105
+ foreach ($recipients as $recipient) {
106
+ $recipient = Validator::email($recipient);
107
+ if ($recipient) {
108
+ $this->mail->{$method}($recipient);
109
+ } else {
110
+ throw new Exception("Invalid email address in $type");
111
+ }
112
+ }
113
+ } else {
114
+ $recipient = Validator::email($recipients);
115
+ if ($recipient) {
116
+ $this->mail->{$method}($recipient);
117
+ } else {
118
+ throw new Exception("Invalid email address in $type");
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Handle adding file attachments.
126
+ *
127
+ * @param string|array $attachments File path(s) to attach.
128
+ * You can pass a string for a single file or an array of file paths.
129
+ * Alternatively, each attachment can be an array with keys 'path' and 'name' for custom naming.
130
+ *
131
+ * @throws Exception Throws an exception if any attachment file is not found.
132
+ */
133
+ private function handleAttachments(string|array $attachments): void
134
+ {
135
+ if (is_array($attachments)) {
136
+ foreach ($attachments as $attachment) {
137
+ if (is_array($attachment)) {
138
+ $file = $attachment['path'] ?? null;
139
+ $name = $attachment['name'] ?? '';
140
+ if (!$file || !file_exists($file)) {
141
+ throw new Exception("Attachment file does not exist: " . ($file ?? 'unknown'));
142
+ }
143
+ $this->mail->addAttachment($file, $name);
144
+ } else {
145
+ if (!file_exists($attachment)) {
146
+ throw new Exception("Attachment file does not exist: $attachment");
147
+ }
148
+ $this->mail->addAttachment($attachment);
149
+ }
150
+ }
151
+ } else {
152
+ if (!file_exists($attachments)) {
153
+ throw new Exception("Attachment file does not exist: $attachments");
154
+ }
155
+ $this->mail->addAttachment($attachments);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Convert HTML content to plain text.
161
+ *
162
+ * @param string $html The HTML content to convert.
163
+ * @return string The plain text content.
164
+ */
165
+ private function convertToPlainText(string $html): string
166
+ {
167
+ return strip_tags(str_replace(['<br>', '<br/>', '<br />', '</p>'], "\n", $html));
168
+ }
169
+ }
@@ -0,0 +1,49 @@
1
+ <?php
2
+
3
+ namespace Lib\PHPX\Exceptions;
4
+
5
+ use RuntimeException;
6
+
7
+ class ComponentValidationException extends RuntimeException
8
+ {
9
+ private string $propName;
10
+ private string $componentName;
11
+ private array $availableProps;
12
+
13
+ public function __construct(
14
+ string $propName,
15
+ string $componentName,
16
+ array $availableProps,
17
+ string $context = ''
18
+ ) {
19
+ $this->propName = $propName;
20
+ $this->componentName = $componentName;
21
+ $this->availableProps = $availableProps;
22
+
23
+ $availableList = implode(', ', $availableProps);
24
+
25
+ $message = "Invalid prop '{$propName}' for component '{$componentName}'.\n";
26
+ $message .= "Available props: {$availableList}";
27
+
28
+ if ($context) {
29
+ $message .= "\n{$context}";
30
+ }
31
+
32
+ parent::__construct($message);
33
+ }
34
+
35
+ public function getPropName(): string
36
+ {
37
+ return $this->propName;
38
+ }
39
+
40
+ public function getComponentName(): string
41
+ {
42
+ return $this->componentName;
43
+ }
44
+
45
+ public function getAvailableProps(): array
46
+ {
47
+ return $this->availableProps;
48
+ }
49
+ }
@@ -0,0 +1,32 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib\PHPX;
6
+
7
+ use Lib\PHPX\PHPX;
8
+
9
+ class Fragment extends PHPX
10
+ {
11
+ /** @property ?string $as = div|span|section|article|nav|header|footer|main|aside */
12
+ public ?string $as = null;
13
+ public ?string $class = '';
14
+
15
+ public function __construct(array $props = [])
16
+ {
17
+ parent::__construct($props);
18
+ }
19
+
20
+ public function render(): string
21
+ {
22
+ if ($this->as !== null) {
23
+ $attributes = $this->getAttributes();
24
+ $class = $this->getMergeClasses($this->class);
25
+ $classAttr = $class ? "class=\"{$class}\"" : '';
26
+
27
+ return "<{$this->as} {$classAttr} {$attributes}>{$this->children}</{$this->as}>";
28
+ }
29
+
30
+ return $this->children;
31
+ }
32
+ }
@@ -0,0 +1,22 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib\PHPX;
6
+
7
+ interface IPHPX
8
+ {
9
+ /**
10
+ * Constructor to initialize the component with the given properties.
11
+ *
12
+ * @param array<string, mixed> $props Optional properties to customize the component.
13
+ */
14
+ public function __construct(array $props = []);
15
+
16
+ /**
17
+ * Renders the component with the given properties and children.
18
+ *
19
+ * @return string The rendered HTML content.
20
+ */
21
+ public function render(): string;
22
+ }