create-prisma-php-app 1.9.15 → 1.9.17
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/composer.json +2 -1
- package/dist/.htaccess +5 -10
- package/dist/bootstrap.php +124 -51
- package/dist/index.js +1 -714
- package/dist/prisma-client-php/index.enc +1 -1
- package/dist/prisma-client-php/index.js +11 -11
- package/dist/settings/project-name.cjs +48 -21
- package/dist/settings/request-methods.php +16 -0
- package/dist/settings/route-request.php +31 -0
- package/dist/src/Lib/Auth/Auth.php +163 -0
- package/dist/src/Lib/Auth/AuthConfig.php +59 -0
- package/dist/src/Lib/Middleware/AuthMiddleware.php +91 -0
- package/dist/src/Lib/StateManager.php +110 -0
- package/dist/src/app/index.php +5 -2
- package/package.json +1 -1
- package/composer-websocket.lock +0 -1993
- package/dist/bootstrap-ajax.php +0 -142
- /package/dist/settings/{restart_websocket.bat → restart-websocket.bat} +0 -0
- /package/dist/settings/{restartWebsocket.cjs → restart-websocket.cjs} +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import*as fs from"fs";import{exec}from"child_process";import path from"path";import{fileURLToPath}from"url";import CryptoJS from"crypto-js";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),getSecretKey=()=>{const r=fs.readFileSync(`${__dirname}/key.enc`,"utf8");if(r.length<400)throw new Error("File content is less than 400 characters.");return r.substring(247,289)},decryptData=(r,t)=>CryptoJS.AES.decrypt(r,t).toString(CryptoJS.enc.Utf8);
|
|
2
2
|
const executePHP = (command) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
exec(command, (error, stdout, stderr) => {
|
|
4
|
+
if (error) {
|
|
5
|
+
console.error(`Execution error: ${error}`);
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (stderr) {
|
|
9
|
+
console.error(`Standard error: ${stderr}`);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log(`Standard output...\n${stdout}`);
|
|
13
|
+
});
|
|
14
14
|
};
|
|
15
15
|
const main=async()=>{try{const e=process.cwd(),n=path.join(e,"prisma-php.json"),a=fs.readFileSync(n,{encoding:"utf8"}),c=JSON.parse(a),t=c.phpGenerateClassPath,i=`${__dirname}/index.php`,p=`${__dirname}/index.enc`,s=getSecretKey(),r=fs.readFileSync(p,{encoding:"utf8"}),d=decryptData(r,s);fs.writeFileSync(`${__dirname}/index.php`,d);const h=`${c.phpRootPathExe} ${i} ${t}`;executePHP(h)}catch(e){}};main().catch((e=>{}));
|
|
@@ -9,6 +9,7 @@ const newProjectName = path.basename(path.join(__dirname, ".."));
|
|
|
9
9
|
|
|
10
10
|
// Function to update the project name and paths in the JSON config
|
|
11
11
|
function updateProjectNameInConfig(filePath, newProjectName) {
|
|
12
|
+
const filePathDir = path.dirname(filePath);
|
|
12
13
|
fs.readFile(filePath, "utf8", (err, data) => {
|
|
13
14
|
if (err) {
|
|
14
15
|
console.error("Error reading the JSON file:", err);
|
|
@@ -17,29 +18,16 @@ function updateProjectNameInConfig(filePath, newProjectName) {
|
|
|
17
18
|
|
|
18
19
|
let config = JSON.parse(data);
|
|
19
20
|
|
|
20
|
-
// Extract the old project name from the projectRootPath
|
|
21
|
-
const oldProjectNameMatch = /[^\\]*$/.exec(config.projectRootPath);
|
|
22
|
-
if (!oldProjectNameMatch) {
|
|
23
|
-
console.error(
|
|
24
|
-
"Could not extract the old project name from projectRootPath."
|
|
25
|
-
);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const oldProjectName = oldProjectNameMatch[0];
|
|
29
|
-
|
|
30
21
|
// Update the projectName
|
|
31
22
|
config.projectName = newProjectName;
|
|
32
23
|
|
|
33
|
-
// Update paths
|
|
34
|
-
config.projectRootPath =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
config.bsTarget =
|
|
39
|
-
config.bsPathRewrite["^/"] =
|
|
40
|
-
oldProjectName,
|
|
41
|
-
newProjectName
|
|
42
|
-
);
|
|
24
|
+
// Update other paths
|
|
25
|
+
config.projectRootPath = filePathDir;
|
|
26
|
+
|
|
27
|
+
const targetPath = getTargetPath(filePathDir, config.phpEnvironment);
|
|
28
|
+
|
|
29
|
+
config.bsTarget = `http://localhost${targetPath}`;
|
|
30
|
+
config.bsPathRewrite["^/"] = targetPath;
|
|
43
31
|
|
|
44
32
|
// Save the updated config back to the JSON file
|
|
45
33
|
fs.writeFile(filePath, JSON.stringify(config, null, 2), "utf8", (err) => {
|
|
@@ -47,10 +35,49 @@ function updateProjectNameInConfig(filePath, newProjectName) {
|
|
|
47
35
|
console.error("Error writing the updated JSON file:", err);
|
|
48
36
|
return;
|
|
49
37
|
}
|
|
50
|
-
console.log(
|
|
38
|
+
console.log(
|
|
39
|
+
"The project name, PHP path, and other paths have been updated successfully."
|
|
40
|
+
);
|
|
51
41
|
});
|
|
52
42
|
});
|
|
53
43
|
}
|
|
54
44
|
|
|
45
|
+
// Function to determine the target path for browser-sync
|
|
46
|
+
function getTargetPath(fullPath, environment) {
|
|
47
|
+
const normalizedPath = path.normalize(fullPath);
|
|
48
|
+
const webDirectories = {
|
|
49
|
+
XAMPP: path.join("htdocs"),
|
|
50
|
+
WAMP: path.join("www"),
|
|
51
|
+
MAMP: path.join("htdocs"),
|
|
52
|
+
LAMP: path.join("var", "www", "html"),
|
|
53
|
+
LEMP: path.join("usr", "share", "nginx", "html"),
|
|
54
|
+
AMPPS: path.join("www"),
|
|
55
|
+
UniformServer: path.join("www"),
|
|
56
|
+
EasyPHP: path.join("data", "localweb"),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const webDir = webDirectories[environment.toUpperCase()];
|
|
60
|
+
if (!webDir) {
|
|
61
|
+
throw new Error(`Unsupported environment: ${environment}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const indexOfWebDir = normalizedPath
|
|
65
|
+
.toLowerCase()
|
|
66
|
+
.indexOf(path.normalize(webDir).toLowerCase());
|
|
67
|
+
if (indexOfWebDir === -1) {
|
|
68
|
+
throw new Error(`Web directory not found in path: ${webDir}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const startIndex = indexOfWebDir + webDir.length;
|
|
72
|
+
const subPath = normalizedPath.slice(startIndex);
|
|
73
|
+
const safeSeparatorRegex = new RegExp(
|
|
74
|
+
path.sep.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"),
|
|
75
|
+
"g"
|
|
76
|
+
);
|
|
77
|
+
const finalPath = subPath.replace(safeSeparatorRegex, "/") + "/";
|
|
78
|
+
|
|
79
|
+
return finalPath;
|
|
80
|
+
}
|
|
81
|
+
|
|
55
82
|
// Run the function with your config file path and the new project name
|
|
56
83
|
updateProjectNameInConfig(configFilePath, newProjectName);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
$requestMethod = $_SERVER['REQUEST_METHOD'];
|
|
4
|
+
$isGet = $requestMethod === 'GET';
|
|
5
|
+
$isPost = $requestMethod === 'POST';
|
|
6
|
+
$isPut = $requestMethod === 'PUT';
|
|
7
|
+
$isDelete = $requestMethod === 'DELETE';
|
|
8
|
+
$isPatch = $requestMethod === 'PATCH';
|
|
9
|
+
$isHead = $requestMethod === 'HEAD';
|
|
10
|
+
$isOptions = $requestMethod === 'OPTIONS';
|
|
11
|
+
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
|
12
|
+
$requestedWith = $_SERVER['HTTP_X_REQUESTED_WITH'] ?? '';
|
|
13
|
+
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
|
|
14
|
+
$domainName = $_SERVER['HTTP_HOST'];
|
|
15
|
+
$scriptName = dirname($_SERVER['SCRIPT_NAME']) . '/';
|
|
16
|
+
$baseUrl = $protocol . $domainName . rtrim($scriptName, '/') . '/src/app/';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
header('Content-Type: application/json');
|
|
4
|
+
|
|
5
|
+
if ($isOptions == 'OPTIONS') {
|
|
6
|
+
header('HTTP/1.1 200 OK');
|
|
7
|
+
exit;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Determine the request method
|
|
11
|
+
$allowedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
|
12
|
+
if (!in_array($requestMethod, $allowedMethods)) {
|
|
13
|
+
echo "Method not allowed\n";
|
|
14
|
+
header("HTTP/1.1 405 Method Not Allowed");
|
|
15
|
+
exit;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
$params = [];
|
|
19
|
+
|
|
20
|
+
if (stripos($contentType, 'application/json') !== false) {
|
|
21
|
+
$jsonInput = file_get_contents('php://input');
|
|
22
|
+
if (!empty($jsonInput)) {
|
|
23
|
+
$data = json_decode($jsonInput, true);
|
|
24
|
+
if (json_last_error() === JSON_ERROR_NONE) {
|
|
25
|
+
$params = $data;
|
|
26
|
+
} else {
|
|
27
|
+
echo json_encode(['error' => 'Error: Invalid JSON body!']);
|
|
28
|
+
exit;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Lib\Auth;
|
|
4
|
+
|
|
5
|
+
use Firebase\JWT\JWT;
|
|
6
|
+
use Firebase\JWT\Key;
|
|
7
|
+
use DateInterval;
|
|
8
|
+
use DateTime;
|
|
9
|
+
use Psr\Log\LoggerInterface;
|
|
10
|
+
|
|
11
|
+
class Auth
|
|
12
|
+
{
|
|
13
|
+
public const PAYLOAD_NAME = 'role';
|
|
14
|
+
public const PAYLOAD = 'payload';
|
|
15
|
+
public const COOKIE_NAME = 'auth_token';
|
|
16
|
+
|
|
17
|
+
private $secretKey;
|
|
18
|
+
private $defaultTokenValidity = '1h'; // Default to 1 hour
|
|
19
|
+
private $logger;
|
|
20
|
+
|
|
21
|
+
public function __construct(LoggerInterface $logger = null)
|
|
22
|
+
{
|
|
23
|
+
$this->secretKey = $_ENV['AUTH_SECRET'];
|
|
24
|
+
$this->logger = $logger;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public function authenticate($role, string $tokenValidity = null): string
|
|
28
|
+
{
|
|
29
|
+
if (!$this->secretKey) {
|
|
30
|
+
throw new \InvalidArgumentException("Secret key is required for authentication.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
$expirationTime = $this->calculateExpirationTime($tokenValidity ?? $this->defaultTokenValidity);
|
|
34
|
+
|
|
35
|
+
if ($role instanceof AuthRole) {
|
|
36
|
+
$role = $role->value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
$payload = [
|
|
40
|
+
self::PAYLOAD_NAME => $role,
|
|
41
|
+
'exp' => $expirationTime,
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// Set the payload in the session
|
|
45
|
+
$_SESSION[self::PAYLOAD] = $payload;
|
|
46
|
+
|
|
47
|
+
// Encode the JWT
|
|
48
|
+
$jwt = JWT::encode($payload, $this->secretKey, 'HS256');
|
|
49
|
+
|
|
50
|
+
if (!headers_sent()) {
|
|
51
|
+
$this->setCookies($jwt, $expirationTime);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return $jwt;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private function calculateExpirationTime(string $duration): int
|
|
58
|
+
{
|
|
59
|
+
$now = new DateTime();
|
|
60
|
+
$interval = $this->convertDurationToInterval($duration);
|
|
61
|
+
$futureDate = $now->add($interval);
|
|
62
|
+
return $futureDate->getTimestamp();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private function convertDurationToInterval(string $duration): DateInterval
|
|
66
|
+
{
|
|
67
|
+
if (preg_match('/^(\d+)(s|m|h|d)$/', $duration, $matches)) {
|
|
68
|
+
$value = (int)$matches[1];
|
|
69
|
+
$unit = $matches[2];
|
|
70
|
+
|
|
71
|
+
switch ($unit) {
|
|
72
|
+
case 's':
|
|
73
|
+
return new DateInterval("PT{$value}S");
|
|
74
|
+
case 'm':
|
|
75
|
+
return new DateInterval("PT{$value}M");
|
|
76
|
+
case 'h':
|
|
77
|
+
return new DateInterval("PT{$value}H");
|
|
78
|
+
case 'd':
|
|
79
|
+
return new DateInterval("P{$value}D");
|
|
80
|
+
default:
|
|
81
|
+
throw new \InvalidArgumentException("Invalid duration format: {$duration}");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
throw new \InvalidArgumentException("Invalid duration format: {$duration}");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public function verifyToken(string $jwt)
|
|
89
|
+
{
|
|
90
|
+
try {
|
|
91
|
+
return JWT::decode($jwt, new Key($this->secretKey, 'HS256'));
|
|
92
|
+
} catch (\Firebase\JWT\ExpiredException $e) {
|
|
93
|
+
if ($this->logger) {
|
|
94
|
+
$this->logger->error("JWT expired: " . $e->getMessage());
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
} catch (\Exception $e) {
|
|
98
|
+
if ($this->logger) {
|
|
99
|
+
$this->logger->error("JWT verification failed: " . $e->getMessage());
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public function refreshToken(string $jwt, string $tokenValidity = null): string
|
|
106
|
+
{
|
|
107
|
+
$decodedToken = $this->verifyToken($jwt);
|
|
108
|
+
|
|
109
|
+
if (!$decodedToken) {
|
|
110
|
+
throw new \InvalidArgumentException("Invalid token.");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
$expirationTime = $this->calculateExpirationTime($tokenValidity ?? $this->defaultTokenValidity);
|
|
114
|
+
|
|
115
|
+
$decodedToken->exp = $expirationTime;
|
|
116
|
+
$newJwt = JWT::encode((array)$decodedToken, $this->secretKey, 'HS256');
|
|
117
|
+
|
|
118
|
+
if (!headers_sent()) {
|
|
119
|
+
$this->setCookies($newJwt, $expirationTime);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return $newJwt;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
protected function setCookies(string $jwt, int $expirationTime)
|
|
126
|
+
{
|
|
127
|
+
if (!headers_sent()) {
|
|
128
|
+
setcookie(self::COOKIE_NAME, $jwt, [
|
|
129
|
+
'expires' => $expirationTime,
|
|
130
|
+
'path' => '/',
|
|
131
|
+
'domain' => '', // Specify your domain
|
|
132
|
+
'secure' => true,
|
|
133
|
+
'httponly' => true,
|
|
134
|
+
'samesite' => 'Lax', // or 'Strict' depending on your requirements
|
|
135
|
+
]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public function logout(string $redirect = null)
|
|
140
|
+
{
|
|
141
|
+
if (isset($_COOKIE[self::COOKIE_NAME])) {
|
|
142
|
+
unset($_COOKIE[self::COOKIE_NAME]);
|
|
143
|
+
setcookie(self::COOKIE_NAME, '', time() - 3600, '/');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (isset($_SESSION[self::PAYLOAD])) {
|
|
147
|
+
unset($_SESSION[self::PAYLOAD]);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if ($redirect) {
|
|
151
|
+
redirect($redirect);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public function getPayload()
|
|
156
|
+
{
|
|
157
|
+
if (isset($_SESSION[self::PAYLOAD])) {
|
|
158
|
+
return $_SESSION[self::PAYLOAD][self::PAYLOAD_NAME];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Lib\Auth;
|
|
4
|
+
|
|
5
|
+
enum AuthRole: string
|
|
6
|
+
{
|
|
7
|
+
case Admin = 'Admin';
|
|
8
|
+
case User = 'User';
|
|
9
|
+
|
|
10
|
+
public function equals($role)
|
|
11
|
+
{
|
|
12
|
+
return $this->value === $role;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class AuthConfig
|
|
17
|
+
{
|
|
18
|
+
public const ROLE_IDENTIFIER = 'role';
|
|
19
|
+
public const IS_ROLE_BASE = true;
|
|
20
|
+
public const IS_TOKEN_AUTO_REFRESH = true;
|
|
21
|
+
|
|
22
|
+
// An array listing the public routes that do not require authentication.
|
|
23
|
+
// Example: public static $publicRoutes = ['/'];
|
|
24
|
+
public static $publicRoutes = ['/'];
|
|
25
|
+
|
|
26
|
+
// An array of private routes that are accessible to all authenticated users
|
|
27
|
+
// without specific role-based access control. Routes should be listed as string paths.
|
|
28
|
+
// Example: public static $privateRoutes = ['profile', 'dashboard/settings'];
|
|
29
|
+
public static $privateRoutes = [];
|
|
30
|
+
|
|
31
|
+
// An associative array mapping specific routes to required user roles for access control.
|
|
32
|
+
// Each route is a key with an array of roles that are allowed access.
|
|
33
|
+
// Format:
|
|
34
|
+
// 'route_path' => [self::ROLE_IDENTIFIER => [AuthRole::Role1, AuthRole::Role2, ...]],
|
|
35
|
+
// Example:
|
|
36
|
+
// public static $roleBasedRoutes = [
|
|
37
|
+
// 'dashboard' => [self::ROLE_IDENTIFIER => [AuthRole::Admin, AuthRole::User]],
|
|
38
|
+
// 'dashboard/users' => [self::ROLE_IDENTIFIER => [AuthRole::Admin]],
|
|
39
|
+
// 'sales' => [self::ROLE_IDENTIFIER => [AuthRole::Admin, AuthRole::User]]
|
|
40
|
+
// ];
|
|
41
|
+
public static $roleBasedRoutes = [];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Checks if the given user role is authorized to access a set of roles.
|
|
45
|
+
*
|
|
46
|
+
* @param string $userRole The role of the user attempting to access the route.
|
|
47
|
+
* @param array $roles An array of AuthRole instances specifying allowed roles.
|
|
48
|
+
* @return bool Returns true if the user role is in the allowed roles, false otherwise.
|
|
49
|
+
*/
|
|
50
|
+
public static function checkAuthRole($userRole, $roles)
|
|
51
|
+
{
|
|
52
|
+
foreach ($roles as $role) {
|
|
53
|
+
if ($userRole === $role->value) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Lib\Middleware;
|
|
4
|
+
|
|
5
|
+
use Lib\Auth\Auth;
|
|
6
|
+
use Lib\Auth\AuthConfig;
|
|
7
|
+
|
|
8
|
+
class AuthMiddleware
|
|
9
|
+
{
|
|
10
|
+
public static function handle($requestUri)
|
|
11
|
+
{
|
|
12
|
+
$requestUri = trim($requestUri);
|
|
13
|
+
if (!self::matches($requestUri)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Check if the user is authorized to access the route or redirect to login
|
|
18
|
+
if (!self::isAuthorized()) {
|
|
19
|
+
redirect('/auth/login');
|
|
20
|
+
exit;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check if the user has the required role to access the route or redirect to denied
|
|
24
|
+
if (AuthConfig::IS_ROLE_BASE && !self::hasRequiredRole($requestUri)) {
|
|
25
|
+
redirect('/denied');
|
|
26
|
+
exit;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected static function matches($requestUri)
|
|
31
|
+
{
|
|
32
|
+
foreach (AuthConfig::$privateRoutes ?? [] as $pattern) {
|
|
33
|
+
if (self::getUriRegex($pattern, $requestUri)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected static function isAuthorized(): bool
|
|
41
|
+
{
|
|
42
|
+
$auth = new Auth();
|
|
43
|
+
$cookieName = Auth::COOKIE_NAME;
|
|
44
|
+
if (!isset($_COOKIE[$cookieName])) {
|
|
45
|
+
unset($_SESSION[Auth::PAYLOAD]);
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
$jwt = $_COOKIE[$cookieName];
|
|
50
|
+
|
|
51
|
+
if (AuthConfig::IS_TOKEN_AUTO_REFRESH) {
|
|
52
|
+
$jwt = $auth->refreshToken($jwt);
|
|
53
|
+
$verifyToken = $auth->verifyToken($jwt);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
$verifyToken = $auth->verifyToken($jwt);
|
|
57
|
+
if ($verifyToken === false) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Access the PAYLOAD_NAME property using the -> operator instead of array syntax
|
|
62
|
+
if (isset($verifyToken->{Auth::PAYLOAD_NAME})) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected static function hasRequiredRole($requestUri): bool
|
|
70
|
+
{
|
|
71
|
+
$auth = new Auth();
|
|
72
|
+
$roleBasedRoutes = AuthConfig::$roleBasedRoutes ?? [];
|
|
73
|
+
foreach ($roleBasedRoutes as $pattern => $data) {
|
|
74
|
+
if (self::getUriRegex($pattern, $requestUri)) {
|
|
75
|
+
$userRole = $auth->getPayload()['userRole'] ?? null;
|
|
76
|
+
if ($userRole !== null && AuthConfig::checkAuthRole($userRole, $data[AuthConfig::ROLE_IDENTIFIER])) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private static function getUriRegex($pattern, $requestUri)
|
|
85
|
+
{
|
|
86
|
+
$pattern = strtolower($pattern);
|
|
87
|
+
$requestUri = strtolower(trim($requestUri));
|
|
88
|
+
$regex = "#^/?" . preg_quote($pattern, '#') . "$#";
|
|
89
|
+
return preg_match($regex, $requestUri);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Lib;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Manages the application state.
|
|
7
|
+
*/
|
|
8
|
+
class StateManager
|
|
9
|
+
{
|
|
10
|
+
private $state;
|
|
11
|
+
private $listeners;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Constructs a new StateManager instance.
|
|
15
|
+
*
|
|
16
|
+
* @param array $initialState The initial state of the application.
|
|
17
|
+
*/
|
|
18
|
+
public function __construct($initialState = [])
|
|
19
|
+
{
|
|
20
|
+
$this->state = $initialState;
|
|
21
|
+
$this->listeners = [];
|
|
22
|
+
$this->loadState();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Retrieves the current state of the application.
|
|
27
|
+
*
|
|
28
|
+
* @param string|null $key The key of the state value to retrieve. If null, returns the entire state.
|
|
29
|
+
* @return mixed|null The state value corresponding to the given key, or null if the key is not found.
|
|
30
|
+
*/
|
|
31
|
+
public function getState($key = null)
|
|
32
|
+
{
|
|
33
|
+
if ($key === null) {
|
|
34
|
+
return $this->state;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return $this->state[$key] ?? null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Updates the application state with the given update.
|
|
42
|
+
*
|
|
43
|
+
* @param array $update The state update to apply.
|
|
44
|
+
* @param bool $saveToStorage Whether to save the updated state to storage.
|
|
45
|
+
*/
|
|
46
|
+
public function setState($update, $saveToStorage = false)
|
|
47
|
+
{
|
|
48
|
+
$this->state = array_merge($this->state, $update);
|
|
49
|
+
foreach ($this->listeners as $listener) {
|
|
50
|
+
call_user_func($listener, $this->state);
|
|
51
|
+
}
|
|
52
|
+
if ($saveToStorage) {
|
|
53
|
+
$this->saveState();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Subscribes a listener to state changes.
|
|
59
|
+
*
|
|
60
|
+
* @param callable $listener The listener function to subscribe.
|
|
61
|
+
* @return callable A function that can be called to unsubscribe the listener.
|
|
62
|
+
*/
|
|
63
|
+
public function subscribe($listener)
|
|
64
|
+
{
|
|
65
|
+
$this->listeners[] = $listener;
|
|
66
|
+
call_user_func($listener, $this->state);
|
|
67
|
+
return function () use ($listener) {
|
|
68
|
+
$this->listeners = array_filter($this->listeners, function ($l) use ($listener) {
|
|
69
|
+
return $l !== $listener;
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Saves the current state to storage.
|
|
76
|
+
*/
|
|
77
|
+
private function saveState()
|
|
78
|
+
{
|
|
79
|
+
$_SESSION['appState'] = json_encode($this->state);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Loads the state from storage, if available.
|
|
84
|
+
*/
|
|
85
|
+
private function loadState()
|
|
86
|
+
{
|
|
87
|
+
if (isset($_SESSION['appState'])) {
|
|
88
|
+
$this->state = json_decode($_SESSION['appState'], true);
|
|
89
|
+
foreach ($this->listeners as $listener) {
|
|
90
|
+
call_user_func($listener, $this->state);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resets the application state.
|
|
97
|
+
*
|
|
98
|
+
* @param bool $clearFromStorage Whether to clear the state from storage.
|
|
99
|
+
*/
|
|
100
|
+
public function resetState($clearFromStorage = false)
|
|
101
|
+
{
|
|
102
|
+
$this->state = [];
|
|
103
|
+
foreach ($this->listeners as $listener) {
|
|
104
|
+
call_user_func($listener, $this->state);
|
|
105
|
+
}
|
|
106
|
+
if ($clearFromStorage) {
|
|
107
|
+
unset($_SESSION['appState']);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
package/dist/src/app/index.php
CHANGED
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
<span class="sr-only">Prisma PHP</span>
|
|
6
6
|
</a>
|
|
7
7
|
<nav class="ml-auto flex gap-4 sm:gap-6">
|
|
8
|
-
<a class="text-sm font-medium hover:underline underline-offset-4" href="
|
|
8
|
+
<a class="text-sm font-medium hover:underline underline-offset-4" href="https://prismaphp.tsnc.tech/features" target="_blank">
|
|
9
9
|
Features
|
|
10
10
|
</a>
|
|
11
|
+
<a class="text-sm font-medium hover:underline underline-offset-4" href="https://prismaphp.tsnc.tech/newsletter" target="_blank">
|
|
12
|
+
Join the Newsletter
|
|
13
|
+
</a>
|
|
11
14
|
<a class="text-sm font-medium hover:underline underline-offset-4" href="https://prismaphp.tsnc.tech/docs?doc=get-started" target="_blank">
|
|
12
15
|
Documentation
|
|
13
16
|
</a>
|
|
@@ -26,7 +29,7 @@
|
|
|
26
29
|
<p class="mx-auto max-w-[700px] text-gray-500 md:text-xl dark:text-gray-400">
|
|
27
30
|
The Next Generation ORM for PHP
|
|
28
31
|
</p>
|
|
29
|
-
<a class="inline-flex h-10 items-center justify-center rounded-md bg-gray-900 px-8 text-sm font-medium text-gray-50 shadow transition-colors hover:bg-gray-900/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-950 disabled:pointer-events-none disabled:opacity-50 dark:bg-gray-50 dark:text-gray-900 dark:hover:bg-gray-50/90 dark:focus-visible:ring-gray-300" href="
|
|
32
|
+
<a class="inline-flex h-10 items-center justify-center rounded-md bg-gray-900 px-8 text-sm font-medium text-gray-50 shadow transition-colors hover:bg-gray-900/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-950 disabled:pointer-events-none disabled:opacity-50 dark:bg-gray-50 dark:text-gray-900 dark:hover:bg-gray-50/90 dark:focus-visible:ring-gray-300" href="https://prismaphp.tsnc.tech/docs?doc=get-started">
|
|
30
33
|
Get Started
|
|
31
34
|
</a>
|
|
32
35
|
</div>
|