create-prisma-php-app 4.3.9 → 4.4.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.
- package/dist/bootstrap.php +26 -18
- package/dist/src/Lib/Auth/Auth.php +6 -5
- package/dist/src/Lib/Auth/AuthConfig.php +1 -0
- package/dist/src/Lib/MCP/mcp-server.php +8 -7
- package/dist/src/Lib/Middleware/CorsMiddleware.php +8 -6
- package/dist/src/Lib/Websocket/ConnectionManager.php +3 -3
- package/dist/src/Lib/Websocket/websocket-server.php +8 -7
- package/package.json +1 -1
package/dist/bootstrap.php
CHANGED
|
@@ -8,7 +8,10 @@ require_once __DIR__ . '/settings/paths.php';
|
|
|
8
8
|
use Dotenv\Dotenv;
|
|
9
9
|
use Lib\Middleware\CorsMiddleware;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
if (file_exists(DOCUMENT_PATH . '/.env')) {
|
|
12
|
+
Dotenv::createImmutable(DOCUMENT_PATH)->safeLoad();
|
|
13
|
+
}
|
|
14
|
+
|
|
12
15
|
CorsMiddleware::handle();
|
|
13
16
|
|
|
14
17
|
if (session_status() === PHP_SESSION_NONE) {
|
|
@@ -27,6 +30,7 @@ use PP\ErrorHandler;
|
|
|
27
30
|
use PP\Attributes\Exposed;
|
|
28
31
|
use PP\Streaming\SSE;
|
|
29
32
|
use PP\Security\RateLimiter;
|
|
33
|
+
use PP\Env;
|
|
30
34
|
|
|
31
35
|
final class Bootstrap extends RuntimeException
|
|
32
36
|
{
|
|
@@ -59,7 +63,7 @@ final class Bootstrap extends RuntimeException
|
|
|
59
63
|
|
|
60
64
|
public static function run(): void
|
|
61
65
|
{
|
|
62
|
-
date_default_timezone_set(
|
|
66
|
+
date_default_timezone_set(Env::string('APP_TIMEZONE', 'UTC'));
|
|
63
67
|
|
|
64
68
|
PrismaPHPSettings::init();
|
|
65
69
|
Request::init();
|
|
@@ -139,7 +143,7 @@ final class Bootstrap extends RuntimeException
|
|
|
139
143
|
|
|
140
144
|
private static function setCsrfCookie(): void
|
|
141
145
|
{
|
|
142
|
-
$secret =
|
|
146
|
+
$secret = Env::string('FUNCTION_CALL_SECRET', 'pp_default_insecure_secret');
|
|
143
147
|
$shouldRegenerate = true;
|
|
144
148
|
|
|
145
149
|
if (isset($_COOKIE['prisma_php_csrf'])) {
|
|
@@ -175,7 +179,7 @@ final class Bootstrap extends RuntimeException
|
|
|
175
179
|
{
|
|
176
180
|
$headerToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
|
177
181
|
$cookieToken = $_COOKIE['prisma_php_csrf'] ?? '';
|
|
178
|
-
$secret =
|
|
182
|
+
$secret = Env::string('FUNCTION_CALL_SECRET', '');
|
|
179
183
|
|
|
180
184
|
if (empty($headerToken) || empty($cookieToken)) {
|
|
181
185
|
self::jsonExit(['success' => false, 'error' => 'CSRF token missing']);
|
|
@@ -217,12 +221,11 @@ final class Bootstrap extends RuntimeException
|
|
|
217
221
|
|
|
218
222
|
private static function determineContentToInclude(): array
|
|
219
223
|
{
|
|
220
|
-
$requestUri = $_SERVER['REQUEST_URI'];
|
|
221
|
-
$requestUri =
|
|
224
|
+
$requestUri = $_SERVER['REQUEST_URI'] ?? '/';
|
|
225
|
+
$requestUri = trim(self::uriExtractor($requestUri));
|
|
222
226
|
|
|
223
227
|
$scriptUrl = explode('?', $requestUri, 2)[0];
|
|
224
|
-
$pathname = $
|
|
225
|
-
$pathname = trim($pathname, '/');
|
|
228
|
+
$pathname = trim($scriptUrl, '/');
|
|
226
229
|
$baseDir = APP_PATH;
|
|
227
230
|
$includePath = '';
|
|
228
231
|
$layoutsToInclude = [];
|
|
@@ -520,7 +523,7 @@ final class Bootstrap extends RuntimeException
|
|
|
520
523
|
{
|
|
521
524
|
$projectName = PrismaPHPSettings::$option->projectName ?? '';
|
|
522
525
|
if (empty($projectName)) {
|
|
523
|
-
return
|
|
526
|
+
return $scriptUrl;
|
|
524
527
|
}
|
|
525
528
|
|
|
526
529
|
$escapedIdentifier = preg_quote($projectName, '/');
|
|
@@ -528,7 +531,7 @@ final class Bootstrap extends RuntimeException
|
|
|
528
531
|
return rtrim(ltrim($matches[1], '/'), '/');
|
|
529
532
|
}
|
|
530
533
|
|
|
531
|
-
return
|
|
534
|
+
return $scriptUrl;
|
|
532
535
|
}
|
|
533
536
|
|
|
534
537
|
private static function findGroupFolder(string $pathname): string
|
|
@@ -713,7 +716,7 @@ final class Bootstrap extends RuntimeException
|
|
|
713
716
|
|
|
714
717
|
private static function checkForDuplicateRoutes(): void
|
|
715
718
|
{
|
|
716
|
-
if (
|
|
719
|
+
if (Env::string('APP_ENV', 'production') === 'production') {
|
|
717
720
|
return;
|
|
718
721
|
}
|
|
719
722
|
|
|
@@ -958,9 +961,9 @@ final class Bootstrap extends RuntimeException
|
|
|
958
961
|
|
|
959
962
|
if (empty($limits)) {
|
|
960
963
|
if ($attribute->requiresAuth) {
|
|
961
|
-
$limits =
|
|
964
|
+
$limits = Env::string('RATE_LIMIT_AUTH', '60/minute');
|
|
962
965
|
} else {
|
|
963
|
-
$limits =
|
|
966
|
+
$limits = Env::string('RATE_LIMIT_RPC', '60/minute');
|
|
964
967
|
}
|
|
965
968
|
}
|
|
966
969
|
|
|
@@ -976,6 +979,7 @@ final class Bootstrap extends RuntimeException
|
|
|
976
979
|
return ['success' => false, 'error' => 'Function not callable from client'];
|
|
977
980
|
}
|
|
978
981
|
|
|
982
|
+
$attribute = self::getExposedAttribute($fn);
|
|
979
983
|
if (!self::validateAccess($attribute)) {
|
|
980
984
|
return ['success' => false, 'error' => 'Permission denied'];
|
|
981
985
|
}
|
|
@@ -996,7 +1000,7 @@ final class Bootstrap extends RuntimeException
|
|
|
996
1000
|
return ['success' => false, 'error' => $e->getMessage()];
|
|
997
1001
|
}
|
|
998
1002
|
|
|
999
|
-
if (
|
|
1003
|
+
if (Env::string('SHOW_ERRORS', 'false') === 'false') {
|
|
1000
1004
|
return ['success' => false, 'error' => 'An error occurred. Please try again later.'];
|
|
1001
1005
|
} else {
|
|
1002
1006
|
return ['success' => false, 'error' => "Function error: {$e->getMessage()}"];
|
|
@@ -1071,7 +1075,7 @@ final class Bootstrap extends RuntimeException
|
|
|
1071
1075
|
return ['success' => false, 'error' => $e->getMessage()];
|
|
1072
1076
|
}
|
|
1073
1077
|
|
|
1074
|
-
if (
|
|
1078
|
+
if (Env::string('SHOW_ERRORS', 'false') === 'false') {
|
|
1075
1079
|
return ['success' => false, 'error' => 'An error occurred. Please try again later.'];
|
|
1076
1080
|
} else {
|
|
1077
1081
|
return ['success' => false, 'error' => "Call error: {$e->getMessage()}"];
|
|
@@ -1319,16 +1323,20 @@ try {
|
|
|
1319
1323
|
}
|
|
1320
1324
|
|
|
1321
1325
|
if (Request::$isWire && !Bootstrap::$secondRequestC69CD) {
|
|
1322
|
-
|
|
1326
|
+
while (ob_get_level() > 0) {
|
|
1327
|
+
ob_end_clean();
|
|
1328
|
+
}
|
|
1323
1329
|
Bootstrap::wireCallback();
|
|
1324
1330
|
}
|
|
1325
1331
|
|
|
1326
1332
|
if ((!Request::$isWire && !Bootstrap::$secondRequestC69CD) && isset(Bootstrap::$requestFilesData[Request::$decodedUri])) {
|
|
1333
|
+
$cacheEnabled = (Env::string('CACHE_ENABLED', 'false') === 'true');
|
|
1334
|
+
|
|
1327
1335
|
$shouldCache = CacheHandler::$isCacheable === true
|
|
1328
|
-
|| (CacheHandler::$isCacheable === null && $
|
|
1336
|
+
|| (CacheHandler::$isCacheable === null && $cacheEnabled);
|
|
1329
1337
|
|
|
1330
1338
|
if ($shouldCache) {
|
|
1331
|
-
CacheHandler::serveCache(Request::$decodedUri, intval(
|
|
1339
|
+
CacheHandler::serveCache(Request::$decodedUri, intval(Env::string('CACHE_TTL', '600')));
|
|
1332
1340
|
}
|
|
1333
1341
|
}
|
|
1334
1342
|
|
|
@@ -15,11 +15,12 @@ use PP\Request;
|
|
|
15
15
|
use Exception;
|
|
16
16
|
use InvalidArgumentException;
|
|
17
17
|
use ArrayObject;
|
|
18
|
+
use PP\Env;
|
|
18
19
|
|
|
19
20
|
class Auth
|
|
20
21
|
{
|
|
21
22
|
public const PAYLOAD_NAME = 'payload_name_8639D';
|
|
22
|
-
public const ROLE_NAME = '';
|
|
23
|
+
public const ROLE_NAME = 'role';
|
|
23
24
|
public const PAYLOAD_SESSION_KEY = 'payload_session_key_2183A';
|
|
24
25
|
|
|
25
26
|
public static string $cookieName = '';
|
|
@@ -27,11 +28,11 @@ class Auth
|
|
|
27
28
|
private static ?Auth $instance = null;
|
|
28
29
|
private const PPAUTH = 'ppauth';
|
|
29
30
|
private string $secretKey;
|
|
30
|
-
private string $defaultTokenValidity =
|
|
31
|
+
private string $defaultTokenValidity = AuthConfig::DEFAULT_TOKEN_VALIDITY;
|
|
31
32
|
|
|
32
33
|
private function __construct()
|
|
33
34
|
{
|
|
34
|
-
$this->secretKey =
|
|
35
|
+
$this->secretKey = Env::string('AUTH_SECRET', 'CD24eEv4qbsC5LOzqeaWbcr58mBMSvA4Mkii8GjRiHkt');
|
|
35
36
|
self::$cookieName = self::getCookieName();
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -255,7 +256,7 @@ class Auth
|
|
|
255
256
|
|
|
256
257
|
public function rotateCsrfToken(): void
|
|
257
258
|
{
|
|
258
|
-
$secret =
|
|
259
|
+
$secret = Env::string('FUNCTION_CALL_SECRET', '');
|
|
259
260
|
|
|
260
261
|
if (empty($secret)) {
|
|
261
262
|
return;
|
|
@@ -546,7 +547,7 @@ class Auth
|
|
|
546
547
|
|
|
547
548
|
private static function getCookieName(): string
|
|
548
549
|
{
|
|
549
|
-
$authCookieName =
|
|
550
|
+
$authCookieName = Env::string('AUTH_COOKIE_NAME', 'auth_cookie_name_d36e5');
|
|
550
551
|
return strtolower(preg_replace('/\s+/', '_', trim($authCookieName)));
|
|
551
552
|
}
|
|
552
553
|
}
|
|
@@ -23,6 +23,7 @@ final class AuthConfig
|
|
|
23
23
|
public const IS_ROLE_BASE = false;
|
|
24
24
|
public const IS_TOKEN_AUTO_REFRESH = false;
|
|
25
25
|
public const IS_ALL_ROUTES_PRIVATE = false;
|
|
26
|
+
public const DEFAULT_TOKEN_VALIDITY = "1h"; // Default to 1 hour
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* This is the (default) option for authentication. If IS_ALL_ROUTES_PRIVATE is set to false,
|
|
@@ -12,20 +12,21 @@ use Dotenv\Dotenv;
|
|
|
12
12
|
use PhpMcp\Server\Server;
|
|
13
13
|
use PhpMcp\Server\Transports\StreamableHttpServerTransport;
|
|
14
14
|
use Throwable;
|
|
15
|
+
use PP\Env;
|
|
15
16
|
|
|
16
17
|
// ── Load .env (optional) and timezone ──────────────────────────────────────────
|
|
17
18
|
if (file_exists(DOCUMENT_PATH . '/.env')) {
|
|
18
19
|
Dotenv::createImmutable(DOCUMENT_PATH)->safeLoad();
|
|
19
20
|
}
|
|
20
|
-
date_default_timezone_set(
|
|
21
|
+
date_default_timezone_set(Env::string('APP_TIMEZONE', 'UTC'));
|
|
21
22
|
|
|
22
23
|
// ── Resolve settings (with sane defaults) ─────────────────────────────────────
|
|
23
|
-
$appName =
|
|
24
|
-
$appVersion =
|
|
25
|
-
$host =
|
|
26
|
-
$port =
|
|
27
|
-
$prefix = trim(
|
|
28
|
-
$enableJson =
|
|
24
|
+
$appName = Env::string('MCP_NAME', 'prisma-php-mcp');
|
|
25
|
+
$appVersion = Env::string('MCP_VERSION', '0.0.1');
|
|
26
|
+
$host = Env::string('MCP_HOST', '127.0.0.1');
|
|
27
|
+
$port = Env::int('MCP_PORT', 4000);
|
|
28
|
+
$prefix = trim(Env::string('MCP_PATH_PREFIX', 'mcp'), '/');
|
|
29
|
+
$enableJson = Env::bool('MCP_JSON_RESPONSE', false);
|
|
29
30
|
|
|
30
31
|
// ── Build server and discover tools ───────────────────────────────────────────
|
|
31
32
|
$server = Server::make()
|
|
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
|
|
4
4
|
|
|
5
5
|
namespace Lib\Middleware;
|
|
6
6
|
|
|
7
|
+
use PP\Env;
|
|
8
|
+
|
|
7
9
|
final class CorsMiddleware
|
|
8
10
|
{
|
|
9
11
|
public static function handle(?array $overrides = null): void
|
|
@@ -58,14 +60,14 @@ final class CorsMiddleware
|
|
|
58
60
|
|
|
59
61
|
private static function buildConfig(?array $overrides): array
|
|
60
62
|
{
|
|
61
|
-
$allowed = self::parseList(
|
|
63
|
+
$allowed = self::parseList(Env::string('CORS_ALLOWED_ORIGINS', ''));
|
|
62
64
|
$cfg = [
|
|
63
65
|
'allowedOrigins' => $allowed,
|
|
64
|
-
'allowCredentials' =>
|
|
65
|
-
'allowedMethods' =>
|
|
66
|
-
'allowedHeaders' => trim(
|
|
67
|
-
'exposeHeaders' => trim(
|
|
68
|
-
'maxAge' =>
|
|
66
|
+
'allowCredentials' => Env::bool('CORS_ALLOW_CREDENTIALS', false),
|
|
67
|
+
'allowedMethods' => Env::string('CORS_ALLOWED_METHODS', 'GET, POST, PUT, PATCH, DELETE, OPTIONS'),
|
|
68
|
+
'allowedHeaders' => trim(Env::string('CORS_ALLOWED_HEADERS', '')),
|
|
69
|
+
'exposeHeaders' => trim(Env::string('CORS_EXPOSE_HEADERS', '')),
|
|
70
|
+
'maxAge' => Env::int('CORS_MAX_AGE', 86400),
|
|
69
71
|
];
|
|
70
72
|
|
|
71
73
|
if (is_array($overrides)) {
|
|
@@ -15,12 +15,12 @@ class ConnectionManager implements MessageComponentInterface
|
|
|
15
15
|
|
|
16
16
|
public function __construct()
|
|
17
17
|
{
|
|
18
|
-
$this->clients = new SplObjectStorage;
|
|
18
|
+
$this->clients = new SplObjectStorage();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
public function onOpen(ConnectionInterface $conn): void
|
|
22
22
|
{
|
|
23
|
-
$this->clients->
|
|
23
|
+
$this->clients->offsetSet($conn, true);
|
|
24
24
|
echo "New connection! ({$conn->resourceId})";
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -35,7 +35,7 @@ class ConnectionManager implements MessageComponentInterface
|
|
|
35
35
|
|
|
36
36
|
public function onClose(ConnectionInterface $conn): void
|
|
37
37
|
{
|
|
38
|
-
$this->clients->
|
|
38
|
+
$this->clients->offsetUnset($conn);
|
|
39
39
|
echo "Connection {$conn->resourceId} has disconnected";
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -15,14 +15,15 @@ use Ratchet\WebSocket\WsServer;
|
|
|
15
15
|
use Lib\Websocket\ConnectionManager;
|
|
16
16
|
use React\EventLoop\LoopInterface;
|
|
17
17
|
use Throwable;
|
|
18
|
+
use PP\Env;
|
|
18
19
|
|
|
19
20
|
// ── Load .env (optional) and timezone ─────────────────────────────────────────
|
|
20
21
|
if (file_exists(DOCUMENT_PATH . '/.env')) {
|
|
21
22
|
Dotenv::createImmutable(DOCUMENT_PATH)->safeLoad();
|
|
22
23
|
}
|
|
23
|
-
date_default_timezone_set(
|
|
24
|
+
date_default_timezone_set(Env::string('APP_TIMEZONE', 'UTC'));
|
|
24
25
|
|
|
25
|
-
// ── Tiny argv parser: allows --host=0.0.0.0 --port=
|
|
26
|
+
// ── Tiny argv parser: allows --host=0.0.0.0 --port=9001 ──────────────────────
|
|
26
27
|
$cli = [];
|
|
27
28
|
foreach ($argv ?? [] as $arg) {
|
|
28
29
|
if (preg_match('/^--([^=]+)=(.*)$/', $arg, $m)) {
|
|
@@ -31,11 +32,11 @@ foreach ($argv ?? [] as $arg) {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
// ── Resolve settings (env → cli defaults) ────────────────────────────────────
|
|
34
|
-
$appName =
|
|
35
|
-
$appVer =
|
|
36
|
-
$host = $cli['host'] ?? (
|
|
37
|
-
$port = (int)($cli['port'] ?? (
|
|
38
|
-
$verbose = filter_var($cli['verbose'] ?? (
|
|
35
|
+
$appName = Env::string('WS_NAME', 'prisma-php-ws');
|
|
36
|
+
$appVer = Env::string('WS_VERSION', '0.0.1');
|
|
37
|
+
$host = $cli['host'] ?? Env::string('WS_HOST', '127.0.0.1');
|
|
38
|
+
$port = (int)($cli['port'] ?? Env::int('WS_PORT', 9001));
|
|
39
|
+
$verbose = filter_var($cli['verbose'] ?? Env::bool('WS_VERBOSE', true), FILTER_VALIDATE_BOOLEAN);
|
|
39
40
|
|
|
40
41
|
// ── Console helpers ──────────────────────────────────────────────────────────
|
|
41
42
|
$color = static fn(string $t, string $c) => "\033[{$c}m{$t}\033[0m";
|