create-prisma-php-app 2.0.0-alpha.8 → 2.0.0-beta.1
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/.htaccess +31 -4
- package/dist/index.js +48 -46
- package/dist/postcss.config.js +1 -0
- package/dist/src/Lib/AI/ChatGPTClient.php +1 -1
- package/dist/src/Lib/Auth/Auth.php +3 -3
- package/dist/src/Lib/Auth/AuthConfig.php +5 -3
- package/dist/src/Lib/Request.php +32 -36
- package/dist/src/Lib/StateManager.php +2 -2
- package/dist/src/Lib/Websocket/ConnectionManager.php +4 -2
- package/dist/src/app/css/tailwind.css +1 -1
- package/package.json +1 -1
- package/dist/prisma/schema.prisma +0 -37
- package/dist/prisma/seed.ts +0 -74
- package/dist/settings/prisma-schema.json +0 -103
- package/dist/settings/prisma-sdk.ts +0 -28
- package/dist/src/Lib/Prisma/Classes/PPHPUtility.php +0 -854
- package/dist/src/Lib/Prisma/Model/IModel.php +0 -24
package/dist/.htaccess
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Turn on rewrite engine
|
|
2
2
|
RewriteEngine On
|
|
3
3
|
|
|
4
|
-
#
|
|
5
|
-
<
|
|
4
|
+
# Prevent access to sensitive files
|
|
5
|
+
<FilesMatch "(^\.htaccess|\.git|\.env|composer\.(json|lock)|package(-lock)?\.json|phpunit\.xml)$">
|
|
6
6
|
Order allow,deny
|
|
7
7
|
Deny from all
|
|
8
|
-
</
|
|
8
|
+
</FilesMatch>
|
|
9
9
|
|
|
10
10
|
# Allow cross-origin requests (CORS) for all routes
|
|
11
11
|
<IfModule mod_headers.c>
|
|
@@ -32,11 +32,38 @@ RewriteEngine On
|
|
|
32
32
|
</FilesMatch>
|
|
33
33
|
</IfModule>
|
|
34
34
|
|
|
35
|
+
# Add important security headers
|
|
36
|
+
<IfModule mod_headers.c>
|
|
37
|
+
# Enforce HTTPS and prevent protocol downgrade attacks
|
|
38
|
+
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
39
|
+
|
|
40
|
+
# Protect against Cross-Site Scripting (XSS) attacks
|
|
41
|
+
Header set X-XSS-Protection "1; mode=block"
|
|
42
|
+
|
|
43
|
+
# Prevent MIME-type sniffing
|
|
44
|
+
Header set X-Content-Type-Options "nosniff"
|
|
45
|
+
|
|
46
|
+
# Clickjacking protection
|
|
47
|
+
Header always set X-Frame-Options "DENY"
|
|
48
|
+
|
|
49
|
+
# Implement a basic Content Security Policy (CSP)
|
|
50
|
+
Header set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;"
|
|
51
|
+
|
|
52
|
+
# Restrict form submissions
|
|
53
|
+
Header set Content-Security-Policy "form-action 'self'"
|
|
54
|
+
|
|
55
|
+
# Set a strict Referrer Policy
|
|
56
|
+
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
|
57
|
+
|
|
58
|
+
# Control browser permissions (optional but recommended)
|
|
59
|
+
Header set Permissions-Policy "geolocation=(), microphone=(), camera=(), autoplay=()"
|
|
60
|
+
</IfModule>
|
|
61
|
+
|
|
35
62
|
# Exclude static files from being redirected
|
|
36
63
|
RewriteCond %{REQUEST_URI} !\.(css|js|png|jpe?g|gif|svg|webp|woff2?|ttf|eot|ico|pdf|mp4|webm|mp3|ogg)$ [NC]
|
|
37
64
|
RewriteCond %{REQUEST_URI} !^/bootstrap.php
|
|
38
65
|
RewriteRule ^(.*)$ bootstrap.php [QSA,L]
|
|
39
66
|
|
|
40
|
-
#
|
|
67
|
+
# Ensure OPTIONS requests are handled correctly
|
|
41
68
|
RewriteCond %{REQUEST_METHOD} OPTIONS
|
|
42
69
|
RewriteRule ^ - [R=200,L]
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";import{randomBytes}from"crypto";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let c=`http://localhost/${n}`;c=c.endsWith("/")?c.slice(0,-1):c;const i=c.replace(/(?<!:)(\/\/+)/g,"/"),o=n.replace(/\/\/+/g,"/");return{bsTarget:`${i}/`,bsPathRewrite:{"^/":`/${o.startsWith("/")?o.substring(1):o}/`}}}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8"));n.scripts={...n.scripts,projectName:"tsx settings/project-name.ts"};let c=[];if(s.tailwindcss&&(n.scripts={...n.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch"},c.push("tailwind")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.docker&&(n.scripts={...n.scripts,docker:"docker-compose up"},c.push("docker")),s.swaggerDocs){const e=s.prisma?"tsx settings/auto-swagger-docs.ts":"tsx settings/swagger-config.ts";n.scripts={...n.scripts,"create-swagger-docs":e}}let i={...n.scripts};i.browserSync="tsx settings/bs-config.ts",i.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,n.scripts=i,n.type="module",s.prisma&&(n.prisma={seed:"tsx prisma/seed.ts"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}async function updateComposerJson(e,s){const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require={...n.require,"cboden/ratchet":"^0.4.4"}),s.prisma&&(n.require={...n.require,"calicastle/cuid":"^2.0.0"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nvar ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}function generateAuthSecret(){return randomBytes(33).toString("base64")}function generateLocalStoreKey(){return randomBytes(16).toString("hex")}function copyRecursiveSync(e,s,t){const n=fs.existsSync(e),c=n&&fs.statSync(e);if(n&&c&&c.isDirectory()){const n=s.toLowerCase();if(!t.websocket&&n.includes("src\\lib\\websocket"))return;if(!t.prisma&&n.includes("src\\lib\\prisma"))return;if(t.backendOnly&&n.includes("src\\app\\js")||t.backendOnly&&n.includes("src\\app\\css")||t.backendOnly&&n.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&n.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((n=>{copyRecursiveSync(path.join(e,n),path.join(s,n),t)}))}else{if(checkExcludeFiles(s))return;if(!t.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!t.websocket&&(s.includes("restart-websocket.ts")||s.includes("restart-websocket.bat")))return;if(!t.docker&&dockerFiles.some((e=>s.includes(e))))return;if(t.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!t.backendOnly&&s.includes("route.php"))return;if(t.backendOnly&&!t.swaggerDocs&&s.includes("layout.php"))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))return;if(!t.prisma&&(s.includes("prisma-sdk.ts")||s.includes("prisma-schema.json")))return;if((!t.swaggerDocs||!t.prisma)&&(s.includes("auto-swagger-docs.ts")||s.includes("prisma-schema-config.json")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,'export default {\n plugins: {\n "@tailwindcss/postcss": {},\n },\n};',{flag:"w"})}function modifyLayoutPHP(e,s){const t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8"),n="";s.backendOnly||(s.tailwindcss||(n='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet" />'),n+='\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet" /> ${n}`:n),e=e.replace("</head>",`${c}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){return!!updateAnswer?.isUpdate&&(updateAnswer?.excludeFilePath?.includes(e.replace(/\\/g,"/"))??!1)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"},{src:"/tsconfig.json",dest:"/tsconfig.json"},{src:"/app-gitignore",dest:"/.gitignore"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.prisma&&n.push({src:"/prisma",dest:"/prisma"}),s.docker&&n.push({src:"/.dockerignore",dest:"/.dockerignore"},{src:"/docker-compose.yml",dest:"/docker-compose.yml"},{src:"/Dockerfile",dest:"/Dockerfile"},{src:"/apache.conf",dest:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),c=path.join(e,t);if(checkExcludeFiles(c))return;const i=fs.readFileSync(n,"utf8");fs.writeFileSync(c,i,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&modifyPostcssConfig(e),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const c=`# Prisma PHP Auth Secret Key For development only - Change this in production\nAUTH_SECRET="${generateAuthSecret()}"\n\n# PHPMailer\n# SMTP_HOST="smtp.gmail.com" or your SMTP host\n# SMTP_USERNAME="john.doe@gmail.com" or your SMTP username\n# SMTP_PASSWORD="123456"\n# SMTP_PORT="587" for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION="ssl" or tls\n# MAIL_FROM="john.doe@gmail.com"\n# MAIL_FROM_NAME="John Doe"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS="true"\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"\n\n# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"\nAPP_ENV="development"\n\n# APP CACHE ENABLED - Set to true to enable caching - Default is false\nCACHE_ENABLED="false"\n# APP CACHE TTL - Set the cache time to live in seconds - Default is 600 seconds (10 minutes)\nCACHE_TTL="600"\n\n# LOCAL STORAGE KEY - Define a custom key for local storage.\n# If not set, it defaults to "pphp_local_store_59e13".\n# Spaces in the value will be replaced with underscores, and the key will be converted to lowercase automatically.\nLOCALSTORE_KEY="${generateLocalStoreKey()}"`;if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${c}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,c)}async function getAnswer(e={}){const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||s.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const t=()=>{process.exit(0)},n=await prompts(s,{onCancel:t}),c=[];n.backendOnly||e.backendOnly?(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||c.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const i=await prompts(c,{onCancel:t});return{projectName:n.projectName?String(n.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:n.backendOnly??e.backendOnly??!1,swaggerDocs:i.swaggerDocs??e.swaggerDocs??!1,tailwindcss:i.tailwindcss??e.tailwindcss??!1,websocket:i.websocket??e.websocket??!1,prisma:i.prisma??e.prisma??!1,docker:i.docker??e.docker??!1}}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const t=e.split(".").map(Number),n=s.split(".").map(Number);for(let e=0;e<t.length;e++){if(t[e]>n[e])return 1;if(t[e]<n[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}
|
|
2
|
+
import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";import{randomBytes}from"crypto";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let c=`http://localhost/${n}`;c=c.endsWith("/")?c.slice(0,-1):c;const i=c.replace(/(?<!:)(\/\/+)/g,"/"),o=n.replace(/\/\/+/g,"/");return{bsTarget:`${i}/`,bsPathRewrite:{"^/":`/${o.startsWith("/")?o.substring(1):o}/`}}}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8"));n.scripts={...n.scripts,projectName:"tsx settings/project-name.ts"};let c=[];if(s.tailwindcss&&(n.scripts={...n.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch"},c.push("tailwind")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.docker&&(n.scripts={...n.scripts,docker:"docker-compose up"},c.push("docker")),s.swaggerDocs){const e=s.prisma?"tsx settings/auto-swagger-docs.ts":"tsx settings/swagger-config.ts";n.scripts={...n.scripts,"create-swagger-docs":e}}let i={...n.scripts};i.browserSync="tsx settings/bs-config.ts",i.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,n.scripts=i,n.type="module",s.prisma&&(n.prisma={seed:"tsx prisma/seed.ts"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}async function updateComposerJson(e,s){const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require={...n.require,"cboden/ratchet":"^0.4.4"}),s.prisma&&(n.require={...n.require,"calicastle/cuid":"^2.0.0"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nvar ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}function generateAuthSecret(){return randomBytes(33).toString("base64")}function generateLocalStoreKey(){return randomBytes(16).toString("hex")}function copyRecursiveSync(e,s,t){const n=fs.existsSync(e),c=n&&fs.statSync(e);if(n&&c&&c.isDirectory()){const n=s.toLowerCase();if(!t.websocket&&n.includes("src\\lib\\websocket"))return;if(t.backendOnly&&n.includes("src\\app\\js")||t.backendOnly&&n.includes("src\\app\\css")||t.backendOnly&&n.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&n.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((n=>{copyRecursiveSync(path.join(e,n),path.join(s,n),t)}))}else{if(checkExcludeFiles(s))return;if(!t.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!t.websocket&&(s.includes("restart-websocket.ts")||s.includes("restart-websocket.bat")))return;if(!t.docker&&dockerFiles.some((e=>s.includes(e))))return;if(t.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!t.backendOnly&&s.includes("route.php"))return;if(t.backendOnly&&!t.swaggerDocs&&s.includes("layout.php"))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))return;if((!t.swaggerDocs||!t.prisma)&&(s.includes("auto-swagger-docs.ts")||s.includes("prisma-schema-config.json")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,'export default {\n plugins: {\n "@tailwindcss/postcss": {},\n },\n};',{flag:"w"})}function modifyLayoutPHP(e,s){const t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8"),n="";s.backendOnly||(s.tailwindcss||(n='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet" />'),n+='\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet" /> ${n}`:n),e=e.replace("</head>",`${c}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){return!!updateAnswer?.isUpdate&&(updateAnswer?.excludeFilePath?.includes(e.replace(/\\/g,"/"))??!1)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"},{src:"/tsconfig.json",dest:"/tsconfig.json"},{src:"/app-gitignore",dest:"/.gitignore"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.docker&&n.push({src:"/.dockerignore",dest:"/.dockerignore"},{src:"/docker-compose.yml",dest:"/docker-compose.yml"},{src:"/Dockerfile",dest:"/Dockerfile"},{src:"/apache.conf",dest:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),c=path.join(e,t);if(checkExcludeFiles(c))return;const i=fs.readFileSync(n,"utf8");fs.writeFileSync(c,i,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&modifyPostcssConfig(e),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const c=`# Prisma PHP Auth Secret Key\nAUTH_SECRET="${generateAuthSecret()}"\n\n# PHPMailer\n# SMTP_HOST="smtp.gmail.com" or your SMTP host\n# SMTP_USERNAME="john.doe@gmail.com" or your SMTP username\n# SMTP_PASSWORD="123456"\n# SMTP_PORT="587" for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION="ssl" or tls\n# MAIL_FROM="john.doe@gmail.com"\n# MAIL_FROM_NAME="John Doe"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS="true"\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"\n\n# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"\nAPP_ENV="development"\n\n# APP CACHE ENABLED - Set to true to enable caching - Default is false\nCACHE_ENABLED="false"\n# APP CACHE TTL - Set the cache time to live in seconds - Default is 600 seconds (10 minutes)\nCACHE_TTL="600"\n\n# LOCAL STORAGE KEY - Define a custom key for local storage.\n# If not set, it defaults to "pphp_local_store_59e13".\n# Spaces in the value will be replaced with underscores, and the key will be converted to lowercase automatically.\nLOCALSTORE_KEY="${generateLocalStoreKey()}"`;if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${c}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,c)}async function getAnswer(e={}){const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||s.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const t=()=>{process.exit(0)},n=await prompts(s,{onCancel:t}),c=[];n.backendOnly||e.backendOnly?(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||c.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const i=await prompts(c,{onCancel:t});return{projectName:n.projectName?String(n.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:n.backendOnly??e.backendOnly??!1,swaggerDocs:i.swaggerDocs??e.swaggerDocs??!1,tailwindcss:i.tailwindcss??e.tailwindcss??!1,websocket:i.websocket??e.websocket??!1,prisma:i.prisma??e.prisma??!1,docker:i.docker??e.docker??!1}}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const t=e.split(".").map(Number),n=s.split(".").map(Number);for(let e=0;e<t.length;e++){if(t[e]>n[e])return 1;if(t[e]<n[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}
|
|
3
3
|
/**
|
|
4
4
|
* Install dependencies in the specified directory.
|
|
5
5
|
* @param {string} baseDir - The base directory where to install the dependencies.
|
|
@@ -32,26 +32,27 @@ async function installDependencies(baseDir, dependencies, isDev = false) {
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
const pinnedVersions = {
|
|
35
|
-
"@prisma/client": "^6.4.
|
|
36
|
-
"@prisma/internals": "^6.4.
|
|
37
|
-
"@tailwindcss/postcss": "^4.0.
|
|
35
|
+
"@prisma/client": "^6.4.1",
|
|
36
|
+
"@prisma/internals": "^6.4.1",
|
|
37
|
+
"@tailwindcss/postcss": "^4.0.9",
|
|
38
38
|
"@types/browser-sync": "^2.29.0",
|
|
39
|
-
"@types/node": "^22.13.
|
|
39
|
+
"@types/node": "^22.13.8",
|
|
40
40
|
"@types/prompts": "^2.4.9",
|
|
41
41
|
autoprefixer: "^10.4.20",
|
|
42
42
|
"browser-sync": "^3.0.3",
|
|
43
43
|
chalk: "^5.4.1",
|
|
44
44
|
"chokidar-cli": "^3.0.0",
|
|
45
|
+
cssnano: "^7.0.6",
|
|
45
46
|
"http-proxy-middleware": "^3.0.3",
|
|
46
47
|
"npm-run-all": "^4.1.5",
|
|
47
48
|
"php-parser": "^3.2.2",
|
|
48
|
-
postcss: "^8.5.
|
|
49
|
+
postcss: "^8.5.3",
|
|
49
50
|
"postcss-cli": "^11.0.0",
|
|
50
|
-
prisma: "^6.4.
|
|
51
|
+
prisma: "^6.4.1",
|
|
51
52
|
prompts: "^2.4.2",
|
|
52
|
-
tailwindcss: "^4.0.
|
|
53
|
+
tailwindcss: "^4.0.9",
|
|
53
54
|
tsx: "^4.19.3",
|
|
54
|
-
typescript: "^5.
|
|
55
|
+
typescript: "^5.8.2",
|
|
55
56
|
};
|
|
56
57
|
function pkg(name) {
|
|
57
58
|
return pinnedVersions[name] ? `${name}@${pinnedVersions[name]}` : name;
|
|
@@ -84,26 +85,28 @@ async function main() {
|
|
|
84
85
|
}
|
|
85
86
|
const currentDir = process.cwd();
|
|
86
87
|
const configPath = path.join(currentDir, "prisma-php.json");
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
88
|
+
if (fs.existsSync(configPath)) {
|
|
89
|
+
const localSettings = readJsonFile(configPath);
|
|
90
|
+
let excludeFiles = [];
|
|
91
|
+
localSettings.excludeFiles?.map((file) => {
|
|
92
|
+
const filePath = path.join(currentDir, file);
|
|
93
|
+
if (fs.existsSync(filePath))
|
|
94
|
+
excludeFiles.push(filePath.replace(/\\/g, "/"));
|
|
95
|
+
});
|
|
96
|
+
updateAnswer = {
|
|
97
|
+
projectName,
|
|
98
|
+
backendOnly: answer?.backendOnly ?? false,
|
|
99
|
+
swaggerDocs: answer?.swaggerDocs ?? false,
|
|
100
|
+
tailwindcss: answer?.tailwindcss ?? false,
|
|
101
|
+
websocket: answer?.websocket ?? false,
|
|
102
|
+
prisma: answer?.prisma ?? false,
|
|
103
|
+
docker: answer?.docker ?? false,
|
|
104
|
+
isUpdate: true,
|
|
105
|
+
excludeFiles: localSettings.excludeFiles ?? [],
|
|
106
|
+
excludeFilePath: excludeFiles ?? [],
|
|
107
|
+
filePath: currentDir,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
107
110
|
} else {
|
|
108
111
|
answer = await getAnswer();
|
|
109
112
|
}
|
|
@@ -124,10 +127,10 @@ async function main() {
|
|
|
124
127
|
latestVersionOfCreatePrismaPhpApp
|
|
125
128
|
) === -1
|
|
126
129
|
) {
|
|
127
|
-
execSync(
|
|
130
|
+
execSync("npm uninstall -g create-prisma-php-app", {
|
|
128
131
|
stdio: "inherit",
|
|
129
132
|
});
|
|
130
|
-
execSync(
|
|
133
|
+
execSync("npm install -g create-prisma-php-app", {
|
|
131
134
|
stdio: "inherit",
|
|
132
135
|
});
|
|
133
136
|
}
|
|
@@ -156,36 +159,31 @@ async function main() {
|
|
|
156
159
|
dependencies.push(pkg("swagger-jsdoc"), pkg("@types/swagger-jsdoc"));
|
|
157
160
|
}
|
|
158
161
|
if (answer.swaggerDocs && answer.prisma) {
|
|
159
|
-
dependencies.push(
|
|
160
|
-
pkg("prompts"),
|
|
161
|
-
pkg("@types/prompts"),
|
|
162
|
-
pkg("@prisma/internals")
|
|
163
|
-
);
|
|
162
|
+
dependencies.push(pkg("prompts"), pkg("@types/prompts"));
|
|
164
163
|
}
|
|
165
164
|
if (answer.tailwindcss) {
|
|
166
165
|
dependencies.push(
|
|
167
166
|
pkg("tailwindcss"),
|
|
168
167
|
pkg("postcss"),
|
|
169
168
|
pkg("postcss-cli"),
|
|
170
|
-
pkg("@tailwindcss/postcss")
|
|
169
|
+
pkg("@tailwindcss/postcss"),
|
|
170
|
+
pkg("cssnano")
|
|
171
171
|
);
|
|
172
172
|
}
|
|
173
173
|
if (answer.websocket) {
|
|
174
174
|
dependencies.push(pkg("chokidar-cli"));
|
|
175
175
|
}
|
|
176
176
|
if (answer.prisma) {
|
|
177
|
-
dependencies.push(pkg("prisma"), pkg("@prisma/client"));
|
|
178
177
|
execSync("npm install -g prisma-client-php", { stdio: "inherit" });
|
|
179
178
|
}
|
|
180
179
|
await installDependencies(projectPath, dependencies, true);
|
|
181
180
|
if (!projectName) {
|
|
182
|
-
execSync(
|
|
181
|
+
execSync("npx tsc --init", { stdio: "inherit" });
|
|
183
182
|
}
|
|
183
|
+
await createDirectoryStructure(projectPath, answer);
|
|
184
184
|
if (answer.prisma) {
|
|
185
|
-
|
|
186
|
-
execSync(`npx prisma init`, { stdio: "inherit" });
|
|
185
|
+
execSync("npx ppo init --prisma-php", { stdio: "inherit" });
|
|
187
186
|
}
|
|
188
|
-
await createDirectoryStructure(projectPath, answer);
|
|
189
187
|
if (answer.swaggerDocs) {
|
|
190
188
|
const swaggerDocsPath = path.join(
|
|
191
189
|
projectPath,
|
|
@@ -260,8 +258,7 @@ async function main() {
|
|
|
260
258
|
"swagger-jsdoc",
|
|
261
259
|
"@types/swagger-jsdoc",
|
|
262
260
|
"prompts",
|
|
263
|
-
"@types/prompts"
|
|
264
|
-
"@prisma/internals"
|
|
261
|
+
"@types/prompts"
|
|
265
262
|
);
|
|
266
263
|
}
|
|
267
264
|
if (!updateAnswer.tailwindcss) {
|
|
@@ -279,7 +276,8 @@ async function main() {
|
|
|
279
276
|
"tailwindcss",
|
|
280
277
|
"postcss",
|
|
281
278
|
"postcss-cli",
|
|
282
|
-
"@tailwindcss/postcss"
|
|
279
|
+
"@tailwindcss/postcss",
|
|
280
|
+
"cssnano"
|
|
283
281
|
);
|
|
284
282
|
}
|
|
285
283
|
if (!updateAnswer.websocket) {
|
|
@@ -309,7 +307,11 @@ async function main() {
|
|
|
309
307
|
updateUninstallDependencies.push("chokidar-cli");
|
|
310
308
|
}
|
|
311
309
|
if (!updateAnswer.prisma) {
|
|
312
|
-
updateUninstallDependencies.push(
|
|
310
|
+
updateUninstallDependencies.push(
|
|
311
|
+
"prisma",
|
|
312
|
+
"@prisma/client",
|
|
313
|
+
"@prisma/internals"
|
|
314
|
+
);
|
|
313
315
|
}
|
|
314
316
|
if (!updateAnswer.docker) {
|
|
315
317
|
const dockerFiles = [
|
package/dist/postcss.config.js
CHANGED
|
@@ -16,7 +16,7 @@ class ChatGPTClient
|
|
|
16
16
|
private string $apiKey = '';
|
|
17
17
|
private array $cache = [];
|
|
18
18
|
|
|
19
|
-
public function __construct(Client $client = null)
|
|
19
|
+
public function __construct(?Client $client = null)
|
|
20
20
|
{
|
|
21
21
|
// Initialize the Guzzle HTTP client, allowing for dependency injection
|
|
22
22
|
$this->client = $client ?: new Client();
|
|
@@ -74,7 +74,7 @@ class Auth
|
|
|
74
74
|
* echo "Error: " . $e->getMessage();
|
|
75
75
|
* }
|
|
76
76
|
*/
|
|
77
|
-
public function signIn($data, string $tokenValidity = null): string
|
|
77
|
+
public function signIn($data, ?string $tokenValidity = null): string
|
|
78
78
|
{
|
|
79
79
|
if (!$this->secretKey) {
|
|
80
80
|
throw new \InvalidArgumentException("Secret key is required for authentication.");
|
|
@@ -210,7 +210,7 @@ class Auth
|
|
|
210
210
|
*
|
|
211
211
|
* @throws InvalidArgumentException Thrown if the token is invalid.
|
|
212
212
|
*/
|
|
213
|
-
public function refreshToken(string $jwt, string $tokenValidity = null): string
|
|
213
|
+
public function refreshToken(string $jwt, ?string $tokenValidity = null): string
|
|
214
214
|
{
|
|
215
215
|
$decodedToken = $this->verifyToken($jwt);
|
|
216
216
|
|
|
@@ -256,7 +256,7 @@ class Auth
|
|
|
256
256
|
*
|
|
257
257
|
* @return void
|
|
258
258
|
*/
|
|
259
|
-
public function signOut(string $redirect = null)
|
|
259
|
+
public function signOut(?string $redirect = null)
|
|
260
260
|
{
|
|
261
261
|
if (isset($_COOKIE[self::COOKIE_NAME])) {
|
|
262
262
|
unset($_COOKIE[self::COOKIE_NAME]);
|
|
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
|
|
4
4
|
|
|
5
5
|
namespace Lib\Auth;
|
|
6
6
|
|
|
7
|
+
use ArrayObject;
|
|
8
|
+
|
|
7
9
|
enum AuthRole: string
|
|
8
10
|
{
|
|
9
11
|
case Admin = 'Admin';
|
|
@@ -67,13 +69,13 @@ final class AuthConfig
|
|
|
67
69
|
/**
|
|
68
70
|
* Checks if the given user role is authorized to access a set of roles.
|
|
69
71
|
*
|
|
70
|
-
* @param
|
|
72
|
+
* @param ArrayObject|string $userRole The user's role to check.
|
|
71
73
|
* @param array<AuthRole> $roles An array of AuthRole instances specifying allowed roles.
|
|
72
74
|
* @return bool Returns true if the user's role matches any of the allowed roles, false otherwise.
|
|
73
75
|
*/
|
|
74
|
-
public static function checkAuthRole(
|
|
76
|
+
public static function checkAuthRole(ArrayObject|string $userRole, array $roles): bool
|
|
75
77
|
{
|
|
76
|
-
if ($userRole instanceof
|
|
78
|
+
if ($userRole instanceof ArrayObject) {
|
|
77
79
|
$userRole = $userRole[Auth::ROLE_NAME] ?? '';
|
|
78
80
|
}
|
|
79
81
|
|
package/dist/src/Lib/Request.php
CHANGED
|
@@ -5,6 +5,9 @@ declare(strict_types=1);
|
|
|
5
5
|
namespace Lib;
|
|
6
6
|
|
|
7
7
|
use Lib\Headers\Boom;
|
|
8
|
+
use ArrayObject;
|
|
9
|
+
use stdClass;
|
|
10
|
+
use Lib\PrismaPHPSettings;
|
|
8
11
|
|
|
9
12
|
class Request
|
|
10
13
|
{
|
|
@@ -16,7 +19,7 @@ class Request
|
|
|
16
19
|
public const baseUrl = '/src/app';
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
|
-
* @var
|
|
22
|
+
* @var stdClass $params A static property to hold request parameters.
|
|
20
23
|
*
|
|
21
24
|
* This property is used to hold request parameters that are passed to the request.
|
|
22
25
|
*
|
|
@@ -28,10 +31,10 @@ class Request
|
|
|
28
31
|
* $id = Request::$params->id;
|
|
29
32
|
* ```
|
|
30
33
|
*/
|
|
31
|
-
public static
|
|
34
|
+
public static ArrayObject $params;
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
|
-
* @var
|
|
37
|
+
* @var stdClass $dynamicParams A static property to hold dynamic parameters.
|
|
35
38
|
*
|
|
36
39
|
* This property is used to hold dynamic parameters that are passed to the request.
|
|
37
40
|
*
|
|
@@ -53,10 +56,10 @@ class Request
|
|
|
53
56
|
*
|
|
54
57
|
* The above code will output the dynamic parameters as an array, which can be useful for debugging purposes.
|
|
55
58
|
*/
|
|
56
|
-
public static
|
|
59
|
+
public static ArrayObject $dynamicParams;
|
|
57
60
|
|
|
58
61
|
/**
|
|
59
|
-
* @var
|
|
62
|
+
* @var stdClass $localStorage A static property to hold request parameters.
|
|
60
63
|
*
|
|
61
64
|
* This property is used to hold request parameters that are passed to the request.
|
|
62
65
|
*
|
|
@@ -68,7 +71,7 @@ class Request
|
|
|
68
71
|
* $id = Request::$localStorage->id;
|
|
69
72
|
* ```
|
|
70
73
|
*/
|
|
71
|
-
public static
|
|
74
|
+
public static ArrayObject $localStorage;
|
|
72
75
|
|
|
73
76
|
/**
|
|
74
77
|
* @var mixed $data Holds request data (e.g., JSON body).
|
|
@@ -189,8 +192,8 @@ class Request
|
|
|
189
192
|
*/
|
|
190
193
|
public static function init(): void
|
|
191
194
|
{
|
|
192
|
-
self::$params = new
|
|
193
|
-
self::$dynamicParams = new
|
|
195
|
+
self::$params = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
|
|
196
|
+
self::$dynamicParams = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
|
|
194
197
|
|
|
195
198
|
self::$referer = $_SERVER['HTTP_REFERER'] ?? 'Unknown';
|
|
196
199
|
self::$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
|
@@ -286,15 +289,15 @@ class Request
|
|
|
286
289
|
/**
|
|
287
290
|
* Get the request parameters.
|
|
288
291
|
*
|
|
289
|
-
* @return
|
|
292
|
+
* @return ArrayObject The request parameters as an ArrayObject with properties.
|
|
290
293
|
*/
|
|
291
|
-
private static function getParams():
|
|
294
|
+
private static function getParams(): ArrayObject
|
|
292
295
|
{
|
|
293
|
-
$params = new
|
|
296
|
+
$params = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
|
|
294
297
|
|
|
295
298
|
switch (self::$method) {
|
|
296
299
|
case 'GET':
|
|
297
|
-
$params = new
|
|
300
|
+
$params = new ArrayObject($_GET, ArrayObject::ARRAY_AS_PROPS);
|
|
298
301
|
break;
|
|
299
302
|
default:
|
|
300
303
|
// Handle JSON input with different variations (e.g., application/json, application/ld+json, etc.)
|
|
@@ -303,7 +306,7 @@ class Request
|
|
|
303
306
|
if ($jsonInput !== false && !empty($jsonInput)) {
|
|
304
307
|
self::$data = json_decode($jsonInput, true);
|
|
305
308
|
if (json_last_error() === JSON_ERROR_NONE) {
|
|
306
|
-
$params = new
|
|
309
|
+
$params = new ArrayObject(self::$data, ArrayObject::ARRAY_AS_PROPS);
|
|
307
310
|
} else {
|
|
308
311
|
Boom::badRequest('Invalid JSON body')->toResponse();
|
|
309
312
|
}
|
|
@@ -315,9 +318,9 @@ class Request
|
|
|
315
318
|
$rawInput = file_get_contents('php://input');
|
|
316
319
|
if ($rawInput !== false && !empty($rawInput)) {
|
|
317
320
|
parse_str($rawInput, $parsedParams);
|
|
318
|
-
$params = new
|
|
321
|
+
$params = new ArrayObject($parsedParams, ArrayObject::ARRAY_AS_PROPS);
|
|
319
322
|
} else {
|
|
320
|
-
$params = new
|
|
323
|
+
$params = new ArrayObject($_POST, ArrayObject::ARRAY_AS_PROPS);
|
|
321
324
|
}
|
|
322
325
|
}
|
|
323
326
|
break;
|
|
@@ -330,28 +333,28 @@ class Request
|
|
|
330
333
|
* Retrieves the local storage data from the session or initializes it if not present.
|
|
331
334
|
*
|
|
332
335
|
* This method checks if the local storage data is available in the static data array or the session.
|
|
333
|
-
* If the data is found, it is decoded from JSON if necessary and returned as an
|
|
334
|
-
* If the data is not found, an empty
|
|
336
|
+
* If the data is found, it is decoded from JSON if necessary and returned as an ArrayObject.
|
|
337
|
+
* If the data is not found, an empty ArrayObject is returned.
|
|
335
338
|
*
|
|
336
|
-
* @return
|
|
339
|
+
* @return ArrayObject The local storage data as an ArrayObject.
|
|
337
340
|
*/
|
|
338
|
-
private static function getLocalStorage():
|
|
341
|
+
private static function getLocalStorage(): ArrayObject
|
|
339
342
|
{
|
|
340
|
-
$sessionKey =
|
|
341
|
-
$localStorage = new
|
|
343
|
+
$sessionKey = PrismaPHPSettings::$localStoreKey;
|
|
344
|
+
$localStorage = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
|
|
342
345
|
|
|
343
346
|
if (isset(self::$data[$sessionKey])) {
|
|
344
347
|
$data = self::$data[$sessionKey];
|
|
345
348
|
|
|
346
349
|
if (is_array($data)) {
|
|
347
350
|
$_SESSION[$sessionKey] = $data;
|
|
348
|
-
$localStorage = new
|
|
351
|
+
$localStorage = new ArrayObject($data, ArrayObject::ARRAY_AS_PROPS);
|
|
349
352
|
} else {
|
|
350
353
|
$decodedData = json_decode($data, true);
|
|
351
354
|
|
|
352
355
|
if (json_last_error() === JSON_ERROR_NONE) {
|
|
353
356
|
$_SESSION[$sessionKey] = $data;
|
|
354
|
-
$localStorage = new
|
|
357
|
+
$localStorage = new ArrayObject($decodedData, ArrayObject::ARRAY_AS_PROPS);
|
|
355
358
|
} else {
|
|
356
359
|
Boom::badRequest('Invalid JSON body')->toResponse();
|
|
357
360
|
}
|
|
@@ -361,12 +364,12 @@ class Request
|
|
|
361
364
|
$sessionData = $_SESSION[$sessionKey];
|
|
362
365
|
|
|
363
366
|
if (is_array($sessionData)) {
|
|
364
|
-
$localStorage = new
|
|
367
|
+
$localStorage = new ArrayObject($sessionData, ArrayObject::ARRAY_AS_PROPS);
|
|
365
368
|
} else {
|
|
366
369
|
$decodedData = json_decode($sessionData, true);
|
|
367
370
|
|
|
368
371
|
if (json_last_error() === JSON_ERROR_NONE) {
|
|
369
|
-
$localStorage = new
|
|
372
|
+
$localStorage = new ArrayObject($decodedData, ArrayObject::ARRAY_AS_PROPS);
|
|
370
373
|
}
|
|
371
374
|
}
|
|
372
375
|
}
|
|
@@ -436,24 +439,17 @@ class Request
|
|
|
436
439
|
*/
|
|
437
440
|
public static function redirect(string $url, bool $replace = true, int $responseCode = 0): void
|
|
438
441
|
{
|
|
439
|
-
// Clean (discard) any previous output
|
|
440
442
|
ob_clean();
|
|
441
|
-
|
|
442
|
-
// Start a fresh output buffer
|
|
443
443
|
ob_start();
|
|
444
444
|
|
|
445
445
|
if (!self::$isWire && !self::$isAjax) {
|
|
446
|
-
|
|
447
|
-
ob_end_clean(); // End the buffer, don't send it
|
|
448
|
-
header("Location: $url", $replace, $responseCode); // Redirect using header
|
|
446
|
+
header("Location: $url", $replace, $responseCode);
|
|
449
447
|
} else {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
ob_end_flush(); // Flush and send the output buffer
|
|
448
|
+
ob_clean();
|
|
449
|
+
echo "redirect_7F834=$url";
|
|
450
|
+
ob_end_flush();
|
|
454
451
|
}
|
|
455
452
|
|
|
456
|
-
// Terminate the script to prevent any further output
|
|
457
453
|
exit;
|
|
458
454
|
}
|
|
459
455
|
}
|
|
@@ -31,7 +31,7 @@ class StateManager
|
|
|
31
31
|
* @param mixed $initialValue The initial value to set if the key does not exist.
|
|
32
32
|
* @return mixed The state value for the specified key.
|
|
33
33
|
*/
|
|
34
|
-
public static function getState(string $key = null, mixed $initialValue = null): mixed
|
|
34
|
+
public static function getState(?string $key = null, mixed $initialValue = null): mixed
|
|
35
35
|
{
|
|
36
36
|
if ($key === null) {
|
|
37
37
|
return new \ArrayObject(self::$state, \ArrayObject::ARRAY_AS_PROPS);
|
|
@@ -100,7 +100,7 @@ class StateManager
|
|
|
100
100
|
*
|
|
101
101
|
* @param string|null $key The key of the state value to reset.
|
|
102
102
|
*/
|
|
103
|
-
public static function resetState(string $key = null): void
|
|
103
|
+
public static function resetState(?string $key = null): void
|
|
104
104
|
{
|
|
105
105
|
if ($key !== null) {
|
|
106
106
|
if (array_key_exists($key, self::$state)) {
|
|
@@ -6,6 +6,8 @@ namespace Lib\Websocket;
|
|
|
6
6
|
|
|
7
7
|
use Ratchet\MessageComponentInterface;
|
|
8
8
|
use Ratchet\ConnectionInterface;
|
|
9
|
+
use Exception;
|
|
10
|
+
use SplObjectStorage;
|
|
9
11
|
|
|
10
12
|
class ConnectionManager implements MessageComponentInterface
|
|
11
13
|
{
|
|
@@ -13,7 +15,7 @@ class ConnectionManager implements MessageComponentInterface
|
|
|
13
15
|
|
|
14
16
|
public function __construct()
|
|
15
17
|
{
|
|
16
|
-
$this->clients = new
|
|
18
|
+
$this->clients = new SplObjectStorage;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
public function onOpen(ConnectionInterface $conn)
|
|
@@ -33,7 +35,7 @@ class ConnectionManager implements MessageComponentInterface
|
|
|
33
35
|
echo "Connection {$conn->resourceId} has disconnected";
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
public function onError(ConnectionInterface $conn,
|
|
38
|
+
public function onError(ConnectionInterface $conn, Exception $e)
|
|
37
39
|
{
|
|
38
40
|
echo "An error has occurred: {$e->getMessage()}";
|
|
39
41
|
$conn->close();
|
package/package.json
CHANGED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// This is your Prisma schema file,
|
|
2
|
-
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
3
|
-
|
|
4
|
-
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
5
|
-
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
6
|
-
|
|
7
|
-
generator client {
|
|
8
|
-
provider = "prisma-client-js"
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
datasource db {
|
|
12
|
-
provider = "postgresql"
|
|
13
|
-
url = env("DATABASE_URL")
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
model User {
|
|
17
|
-
id String @id @default(cuid())
|
|
18
|
-
name String?
|
|
19
|
-
email String? @unique
|
|
20
|
-
password String?
|
|
21
|
-
emailVerified DateTime?
|
|
22
|
-
image String?
|
|
23
|
-
createdAt DateTime @default(now())
|
|
24
|
-
updatedAt DateTime @updatedAt
|
|
25
|
-
|
|
26
|
-
roleId Int?
|
|
27
|
-
userRole UserRole? @relation(fields: [roleId], references: [id])
|
|
28
|
-
|
|
29
|
-
@@map("Users")
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
model UserRole {
|
|
33
|
-
id Int @id @default(autoincrement())
|
|
34
|
-
name String @unique
|
|
35
|
-
|
|
36
|
-
user User[]
|
|
37
|
-
}
|