create-prisma-php-app 5.0.0-alpha.3 → 5.0.0-alpha.30
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/.github/copilot-instructions.md +97 -0
- package/dist/AGENTS.md +434 -131
- package/dist/bootstrap.php +49 -4
- package/dist/index.js +2 -2
- package/dist/prisma-php.js +2 -2
- package/dist/public/js/pp-reactive-v2.js +1 -1
- package/dist/src/Lib/Auth/Auth.php +53 -12
- package/dist/src/Lib/Middleware/AuthMiddleware.php +2 -2
- package/dist/src/Lib/Middleware/CorsMiddleware.php +4 -0
- package/package.json +1 -1
- package/dist/README.md +0 -213
|
@@ -22,6 +22,7 @@ class Auth
|
|
|
22
22
|
public const PAYLOAD_NAME = 'payload_name_8639D';
|
|
23
23
|
public const ROLE_NAME = 'role';
|
|
24
24
|
public const PAYLOAD_SESSION_KEY = 'payload_session_key_2183A';
|
|
25
|
+
private const OAUTH_STATE_SESSION_KEY = 'oauth_state_61e4a';
|
|
25
26
|
|
|
26
27
|
public static string $cookieName = '';
|
|
27
28
|
|
|
@@ -32,7 +33,10 @@ class Auth
|
|
|
32
33
|
|
|
33
34
|
private function __construct()
|
|
34
35
|
{
|
|
35
|
-
$this->secretKey = Env::string('AUTH_SECRET', '
|
|
36
|
+
$this->secretKey = Env::string('AUTH_SECRET', '');
|
|
37
|
+
if ($this->secretKey === '') {
|
|
38
|
+
throw new InvalidArgumentException('AUTH_SECRET is required for authentication.');
|
|
39
|
+
}
|
|
36
40
|
self::$cookieName = self::getCookieName();
|
|
37
41
|
}
|
|
38
42
|
|
|
@@ -117,7 +121,7 @@ class Auth
|
|
|
117
121
|
|
|
118
122
|
$jwt = $_COOKIE[self::$cookieName];
|
|
119
123
|
$verifyToken = $this->verifyToken($jwt);
|
|
120
|
-
if ($verifyToken ===
|
|
124
|
+
if ($verifyToken === null) {
|
|
121
125
|
return false;
|
|
122
126
|
}
|
|
123
127
|
|
|
@@ -258,8 +262,8 @@ class Auth
|
|
|
258
262
|
{
|
|
259
263
|
$secret = Env::string('FUNCTION_CALL_SECRET', '');
|
|
260
264
|
|
|
261
|
-
if (
|
|
262
|
-
|
|
265
|
+
if ($secret === '') {
|
|
266
|
+
throw new InvalidArgumentException('FUNCTION_CALL_SECRET is required for CSRF protection.');
|
|
263
267
|
}
|
|
264
268
|
|
|
265
269
|
$nonce = bin2hex(random_bytes(16));
|
|
@@ -386,26 +390,34 @@ class Auth
|
|
|
386
390
|
{
|
|
387
391
|
$dynamicRouteParams = Request::$dynamicParams[self::PPAUTH] ?? [];
|
|
388
392
|
|
|
389
|
-
if (Request::$isGet && in_array('signin', $dynamicRouteParams)) {
|
|
393
|
+
if (Request::$isGet && in_array('signin', $dynamicRouteParams, true)) {
|
|
390
394
|
foreach ($providers as $provider) {
|
|
391
|
-
if ($provider instanceof GithubProvider && in_array('github', $dynamicRouteParams)) {
|
|
392
|
-
$
|
|
395
|
+
if ($provider instanceof GithubProvider && in_array('github', $dynamicRouteParams, true)) {
|
|
396
|
+
$state = $this->createOAuthState('github');
|
|
397
|
+
$githubAuthUrl = "https://github.com/login/oauth/authorize?scope=user:email%20read:user&client_id={$provider->clientId}&state=" . urlencode($state);
|
|
393
398
|
Request::redirect($githubAuthUrl);
|
|
394
|
-
} elseif ($provider instanceof GoogleProvider && in_array('google', $dynamicRouteParams)) {
|
|
399
|
+
} elseif ($provider instanceof GoogleProvider && in_array('google', $dynamicRouteParams, true)) {
|
|
400
|
+
$state = $this->createOAuthState('google');
|
|
395
401
|
$googleAuthUrl = "https://accounts.google.com/o/oauth2/v2/auth?"
|
|
396
402
|
. "scope=" . urlencode('email profile') . "&"
|
|
397
403
|
. "response_type=code&"
|
|
398
404
|
. "client_id=" . urlencode($provider->clientId) . "&"
|
|
399
|
-
. "redirect_uri=" . urlencode($provider->redirectUri)
|
|
405
|
+
. "redirect_uri=" . urlencode($provider->redirectUri) . "&"
|
|
406
|
+
. "state=" . urlencode($state);
|
|
400
407
|
Request::redirect($googleAuthUrl);
|
|
401
408
|
}
|
|
402
409
|
}
|
|
403
410
|
}
|
|
404
411
|
|
|
405
412
|
$authCode = Validator::string($_GET['code'] ?? '');
|
|
413
|
+
$authState = Validator::string($_GET['state'] ?? '', false);
|
|
414
|
+
|
|
415
|
+
if (Request::$isGet && in_array('callback', $dynamicRouteParams, true) && $authCode !== '') {
|
|
416
|
+
if (in_array('github', $dynamicRouteParams, true)) {
|
|
417
|
+
if (!$this->consumeOAuthState('github', $authState)) {
|
|
418
|
+
exit("Error occurred. Please try again.");
|
|
419
|
+
}
|
|
406
420
|
|
|
407
|
-
if (Request::$isGet && in_array('callback', $dynamicRouteParams) && isset($authCode)) {
|
|
408
|
-
if (in_array('github', $dynamicRouteParams)) {
|
|
409
421
|
$provider = $this->findProvider($providers, GithubProvider::class);
|
|
410
422
|
|
|
411
423
|
if (!$provider) {
|
|
@@ -413,7 +425,11 @@ class Auth
|
|
|
413
425
|
}
|
|
414
426
|
|
|
415
427
|
return $this->githubProvider($provider, $authCode);
|
|
416
|
-
} elseif (in_array('google', $dynamicRouteParams)) {
|
|
428
|
+
} elseif (in_array('google', $dynamicRouteParams, true)) {
|
|
429
|
+
if (!$this->consumeOAuthState('google', $authState)) {
|
|
430
|
+
exit("Error occurred. Please try again.");
|
|
431
|
+
}
|
|
432
|
+
|
|
417
433
|
$provider = $this->findProvider($providers, GoogleProvider::class);
|
|
418
434
|
|
|
419
435
|
if (!$provider) {
|
|
@@ -554,6 +570,31 @@ class Auth
|
|
|
554
570
|
}
|
|
555
571
|
}
|
|
556
572
|
|
|
573
|
+
private function createOAuthState(string $provider): string
|
|
574
|
+
{
|
|
575
|
+
$state = bin2hex(random_bytes(16));
|
|
576
|
+
$_SESSION[self::OAUTH_STATE_SESSION_KEY][$provider] = $state;
|
|
577
|
+
|
|
578
|
+
return $state;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private function consumeOAuthState(string $provider, string $state): bool
|
|
582
|
+
{
|
|
583
|
+
$expectedState = $_SESSION[self::OAUTH_STATE_SESSION_KEY][$provider] ?? '';
|
|
584
|
+
|
|
585
|
+
if (!is_string($expectedState) || $expectedState === '' || $state === '' || !hash_equals($expectedState, $state)) {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
unset($_SESSION[self::OAUTH_STATE_SESSION_KEY][$provider]);
|
|
590
|
+
|
|
591
|
+
if (empty($_SESSION[self::OAUTH_STATE_SESSION_KEY])) {
|
|
592
|
+
unset($_SESSION[self::OAUTH_STATE_SESSION_KEY]);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
|
|
557
598
|
private static function getCookieName(): string
|
|
558
599
|
{
|
|
559
600
|
$authCookieName = Env::string('AUTH_COOKIE_NAME', 'auth_cookie_name_d36e5');
|
|
@@ -123,11 +123,11 @@ final class AuthMiddleware
|
|
|
123
123
|
|
|
124
124
|
$verifyToken = $auth->verifyToken($jwt);
|
|
125
125
|
|
|
126
|
-
if ($verifyToken ===
|
|
126
|
+
if ($verifyToken === null) {
|
|
127
127
|
return false;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
return
|
|
130
|
+
return true;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
protected static function hasRequiredRole(string $requestPathname): string
|
|
@@ -17,6 +17,10 @@ final class CorsMiddleware
|
|
|
17
17
|
|
|
18
18
|
$cfg = self::buildConfig($overrides);
|
|
19
19
|
|
|
20
|
+
if ($cfg['allowCredentials'] && self::listHasWildcard($cfg['allowedOrigins'])) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
if (!self::isAllowedOrigin($origin, $cfg['allowedOrigins'])) {
|
|
21
25
|
return;
|
|
22
26
|
}
|
package/package.json
CHANGED
package/dist/README.md
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
# Prisma PHP
|
|
2
|
-
|
|
3
|
-
Prisma PHP is a modern full-stack PHP framework that combines native PHP, PulsePoint reactivity, PHPX components, and a Prisma-inspired ORM into one cohesive developer experience.
|
|
4
|
-
|
|
5
|
-
Build reactive interfaces with a server-first mental model, structured routing, type-safe data access, and a project layout designed for real applications.
|
|
6
|
-
|
|
7
|
-
## Getting Started
|
|
8
|
-
|
|
9
|
-
Create a new Prisma PHP project:
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npx create-prisma-php-app@latest my-app
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Start the development server:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm run dev
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Open the local app in your browser after the dev server starts.
|
|
22
|
-
|
|
23
|
-
## Prerequisites
|
|
24
|
-
|
|
25
|
-
Before creating a Prisma PHP project, make sure you have:
|
|
26
|
-
|
|
27
|
-
- Node.js 22.x or higher
|
|
28
|
-
- PHP 8.2 or higher
|
|
29
|
-
- Composer 2.x or higher
|
|
30
|
-
- XAMPP or another local PHP environment
|
|
31
|
-
|
|
32
|
-
If you are using XAMPP on Windows, enabling `extension=zip` in `php.ini` is recommended so Composer dependencies install correctly.
|
|
33
|
-
|
|
34
|
-
## What Prisma PHP Includes
|
|
35
|
-
|
|
36
|
-
Prisma PHP brings together the core pieces needed to build full-stack PHP apps:
|
|
37
|
-
|
|
38
|
-
- **Native PHP + modern reactivity** with PulsePoint
|
|
39
|
-
- **PHPX component system** for reusable UI composition
|
|
40
|
-
- **Prisma PHP ORM** for schema-first, type-safe database access
|
|
41
|
-
- **Built-in authentication patterns** for sessions, route protection, RBAC, credentials auth, and provider login
|
|
42
|
-
- **File-based routing** with clear route file conventions
|
|
43
|
-
- **CLI scaffolding** for new apps, starter kits, and optional features
|
|
44
|
-
- **Flexible deployment options** for local development and production workflows
|
|
45
|
-
|
|
46
|
-
## Common Create Commands
|
|
47
|
-
|
|
48
|
-
Create a default full-stack app:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
npx create-prisma-php-app@latest my-app
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Create a project with common options:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
npx create-prisma-php-app@latest my-app --tailwindcss --typescript
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Use a starter kit:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
npx create-prisma-php-app my-app --starter-kit=fullstack
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Other documented flags may include capabilities such as WebSocket support, MCP support, backend-only mode, Swagger docs, and Prisma integration depending on the installed version.
|
|
67
|
-
|
|
68
|
-
## Documentation
|
|
69
|
-
|
|
70
|
-
Prisma PHP ships with local documentation for the installed project version.
|
|
71
|
-
|
|
72
|
-
The installed docs live here:
|
|
73
|
-
|
|
74
|
-
```txt
|
|
75
|
-
node_modules/prisma-php/dist/docs
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Treat these installed docs as the primary documentation source for the current project version.
|
|
79
|
-
|
|
80
|
-
You can also explore the public docs site:
|
|
81
|
-
|
|
82
|
-
```txt
|
|
83
|
-
https://prismaphp.tsnc.tech/
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## AI Quick Start
|
|
87
|
-
|
|
88
|
-
If you are using AI-assisted development in a Prisma PHP project:
|
|
89
|
-
|
|
90
|
-
1. Read `./prisma-php.json` first.
|
|
91
|
-
2. Read the installed docs in `node_modules/prisma-php/dist/docs`.
|
|
92
|
-
3. Read `AGENTS.md` for task-routing rules, framework constraints, and code-generation guidance.
|
|
93
|
-
4. Inspect `vendor/tsnc/prisma-php/src` only when the docs do not answer the task.
|
|
94
|
-
|
|
95
|
-
### Default interactive UI rule
|
|
96
|
-
|
|
97
|
-
In Prisma PHP, AI should treat **PulsePoint as the default frontend interactivity model**.
|
|
98
|
-
|
|
99
|
-
For normal full-stack page work:
|
|
100
|
-
|
|
101
|
-
- render the page with `index.php`
|
|
102
|
-
- manage browser-side state and UI behavior with **PulsePoint**
|
|
103
|
-
- call PHP from the frontend with **`pp.fetchFunction(...)`**
|
|
104
|
-
- expose callable PHP functions with **`#[Exposed]`**
|
|
105
|
-
- validate incoming data on the PHP side with **`PP\Validator`**
|
|
106
|
-
|
|
107
|
-
Do **not** default to PHP-only refresh cycles, handcrafted `fetch('/api/...')` patterns, or extra `route.php` handlers for normal interactive page behavior when PulsePoint plus `pp.fetchFunction(...)` already fits the task.
|
|
108
|
-
|
|
109
|
-
Only prefer a more PHP-only interaction style when the **user explicitly asks for PHP-only behavior**, or when the task is clearly non-reactive.
|
|
110
|
-
|
|
111
|
-
## Project Capability Manifest
|
|
112
|
-
|
|
113
|
-
Prisma PHP uses `prisma-php.json` at the repository root as the source of truth for enabled framework features and local environment configuration.
|
|
114
|
-
|
|
115
|
-
Use it to verify capabilities such as:
|
|
116
|
-
|
|
117
|
-
- Tailwind CSS support
|
|
118
|
-
- backend-only mode
|
|
119
|
-
- Prisma ORM support
|
|
120
|
-
- Swagger docs
|
|
121
|
-
- WebSocket support
|
|
122
|
-
- MCP support
|
|
123
|
-
- TypeScript support
|
|
124
|
-
- local development paths and BrowserSync settings
|
|
125
|
-
|
|
126
|
-
Do not assume a feature is enabled unless `prisma-php.json` confirms it.
|
|
127
|
-
|
|
128
|
-
## Documentation Map
|
|
129
|
-
|
|
130
|
-
Use these docs as the main entry points for common work:
|
|
131
|
-
|
|
132
|
-
- `index.md` for the general documentation entry point
|
|
133
|
-
- `project-structure.md` for project structure, route placement, and file conventions
|
|
134
|
-
- `layouts-and-pages.md` for pages, layouts, nested routes, and dynamic routes
|
|
135
|
-
- `components.md` for PHPX components, props, children, fragments, icons, buttons, and composition
|
|
136
|
-
- `fetching-data.md` for `pp.fetchFunction(...)`, `#[Exposed]`, and interactive backend flows
|
|
137
|
-
- `prisma-php-orm.md` for Prisma ORM, `schema.prisma`, migrations, and generated PHP classes
|
|
138
|
-
- `authentication.md` for auth strategy, sessions, RBAC, credentials auth, and provider flows
|
|
139
|
-
- `file-manager.md` for uploads, `multipart/form-data`, `$_FILES`, and `PP\FileManager\UploadFile`
|
|
140
|
-
- `route-handlers.md` for `route.php`, JSON responses, and direct server handlers
|
|
141
|
-
- `error-handling.md` for expected errors, `error.php`, `not-found.php`, and validation failures
|
|
142
|
-
- `caching.md` for cache behavior and `CacheHandler`
|
|
143
|
-
- `metadata-and-og-images.md` for metadata, title, description, icons, and head behavior
|
|
144
|
-
- `pulsepoint.md` for PulsePoint runtime usage and reactivity patterns
|
|
145
|
-
- `upgrading.md` for feature enablement and project update workflows
|
|
146
|
-
|
|
147
|
-
## Recommended Development Approach
|
|
148
|
-
|
|
149
|
-
When working in Prisma PHP:
|
|
150
|
-
|
|
151
|
-
- prefer the installed Prisma PHP docs over assumptions from other frameworks
|
|
152
|
-
- use `AGENTS.md` as the strict AI operating guide
|
|
153
|
-
- inspect nearby project files before generating new framework-specific code
|
|
154
|
-
- inspect framework internals only when the installed docs are not enough
|
|
155
|
-
|
|
156
|
-
## Route and File Conventions
|
|
157
|
-
|
|
158
|
-
Prisma PHP uses file-based routing with special route files such as:
|
|
159
|
-
|
|
160
|
-
- `index.php` for rendered UI routes
|
|
161
|
-
- `layout.php` for shared UI wrappers
|
|
162
|
-
- `route.php` for direct handlers such as JSON or API-style endpoints
|
|
163
|
-
- `loading.php` for loading UI
|
|
164
|
-
- `not-found.php` for route-level not-found UI
|
|
165
|
-
- `error.php` for route-level or app-level error UI
|
|
166
|
-
|
|
167
|
-
For task-specific route decision rules and framework generation rules, read `AGENTS.md`.
|
|
168
|
-
|
|
169
|
-
## PulsePoint and Frontend Reactivity
|
|
170
|
-
|
|
171
|
-
Prisma PHP uses PulsePoint for browser-side reactivity, and AI should assume this is the **default choice** for interactive UI behavior in full-stack routes.
|
|
172
|
-
|
|
173
|
-
When working with runtime features such as:
|
|
174
|
-
|
|
175
|
-
- `pp.state`
|
|
176
|
-
- `pp.effect`
|
|
177
|
-
- `pp.ref`
|
|
178
|
-
- `pp-for`
|
|
179
|
-
- `pp-spread`
|
|
180
|
-
- `pp-ref`
|
|
181
|
-
|
|
182
|
-
read the installed Prisma PHP docs for the current version first, then consult:
|
|
183
|
-
|
|
184
|
-
```txt
|
|
185
|
-
https://pulsepoint.tsnc.tech/llms
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## Project Structure
|
|
189
|
-
|
|
190
|
-
A generated Prisma PHP project typically includes folders like these:
|
|
191
|
-
|
|
192
|
-
```text
|
|
193
|
-
prisma-php-project/
|
|
194
|
-
├── prisma/ # schema, migrations, seed files
|
|
195
|
-
├── public/ # public entry point and assets
|
|
196
|
-
├── settings/ # project configuration
|
|
197
|
-
├── src/ # application source code
|
|
198
|
-
├── package.json # frontend/dev scripts
|
|
199
|
-
├── composer.json # PHP dependencies
|
|
200
|
-
└── prisma-php.json # Prisma PHP project capability manifest
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## Updating Existing Projects
|
|
204
|
-
|
|
205
|
-
When enabling features or syncing framework-managed project files:
|
|
206
|
-
|
|
207
|
-
1. Update `prisma-php.json` first.
|
|
208
|
-
2. Read `upgrading.md` in the installed docs.
|
|
209
|
-
3. Run the documented project update workflow for the current version.
|
|
210
|
-
|
|
211
|
-
## Learn More
|
|
212
|
-
|
|
213
|
-
Start with the installed docs for the current project version, use the topic-specific markdown guides for focused work, and rely on `AGENTS.md` when strict AI generation rules are needed.
|