create-prisma-php-app 1.25.2 → 1.25.501
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 +153 -216
- package/dist/index.js +2 -2
- package/dist/settings/paths.php +0 -4
- package/dist/src/Lib/AI/ChatGPTClient.php +4 -4
- package/dist/src/Lib/Auth/Auth.php +26 -27
- package/dist/src/Lib/FileManager/UploadFile.php +2 -2
- package/dist/src/Lib/Headers/Boom.php +3 -3
- package/dist/src/Lib/MainLayout.php +38 -18
- package/dist/src/Lib/Middleware/AuthMiddleware.php +19 -18
- package/dist/src/Lib/PrismaPHPSettings.php +43 -0
- package/dist/src/Lib/Request.php +346 -0
- package/dist/src/Lib/StateManager.php +34 -52
- package/dist/src/app/index.php +4 -2
- package/dist/src/app/js/index.js +1 -1
- package/dist/src/app/js/json5.min.js +1 -0
- package/dist/src/app/layout.php +5 -4
- package/dist/src/app/swagger-docs-layout.php +26 -0
- package/package.json +1 -1
- package/dist/settings/public-functions.php +0 -159
- package/dist/settings/request-methods.php +0 -129
- package/dist/swagger-docs-layout.php +0 -17
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";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=[];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&&(n.scripts={...n.scripts,"create-swagger-docs":"tsx settings/swagger-config.ts"});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,"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"}),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")}async function createUpdateGitignoreFile(e,s){const t=path.join(e,".gitignore");if(checkExcludeFiles(t))return;let n="";s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}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"))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.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))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 createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");t=t.replace(/content: \[\],/g,`content: [\n${n}\n],`),fs.writeFileSync(s,t,{flag:"w"})}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,"export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n cssnano: {},\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||(n=s.tailwindcss?'\n <script src="https://cdn.jsdelivr.net/npm/json5@2.2.3/dist/index.min.js"><\/script>\n <script src="<?= $baseUrl; ?>/js/index.js" type="module"><\/script>':'\n <link href="<?= $baseUrl; ?>/css/index.css" rel="stylesheet">\n <script src="<?= $baseUrl; ?>/js/index.js" type="module"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= $baseUrl; ?>/css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`);const i=c.length>0?"\n":"";e=e.replace("</head>",`${c}${i} \x3c!-- Dynamic Head --\x3e\n <?= MainLayout::outputHeadScripts() ?>\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"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.backendOnly&&s.swaggerDocs&&n.push({src:"/swagger-docs-layout.php",dest:"/src/app/layout.php"}),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&&(createOrUpdateTailwindConfig(e),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=uxsjXVPHN038DEYls2Kw0QUgBcXKUyrjv416nIFWPY4= \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';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);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}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:!0,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"}));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";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=[];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&&(n.scripts={...n.scripts,"create-swagger-docs":"tsx settings/swagger-config.ts"});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,"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"}),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")}async function createUpdateGitignoreFile(e,s){const t=path.join(e,".gitignore");if(checkExcludeFiles(t))return;let n="";s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}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"))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")||s.includes("swagger-docs-layout.php")))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))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 createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");t=t.replace(/content: \[\],/g,`content: [\n${n}\n],`),fs.writeFileSync(s,t,{flag:"w"})}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,"export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n cssnano: {},\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);const i=c.length>0?"\n":"";e=e.replace("</head>",`${c}${i} \x3c!-- Dynamic Head --\x3e\n <?= MainLayout::outputHeadScripts() ?>\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"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.backendOnly&&s.swaggerDocs&&n.push({src:"/src/app/swagger-docs-layout.php",dest:"/src/app/layout.php"}),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&&(createOrUpdateTailwindConfig(e),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=uxsjXVPHN038DEYls2Kw0QUgBcXKUyrjv416nIFWPY4= \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';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);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}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:!0,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"}));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.
|
|
@@ -340,7 +340,7 @@ async function main() {
|
|
|
340
340
|
`${chalk.green(
|
|
341
341
|
"Success!"
|
|
342
342
|
)} Prisma PHP project successfully created in ${chalk.green(
|
|
343
|
-
answer.projectName
|
|
343
|
+
`${currentDir}/${answer.projectName}`
|
|
344
344
|
)}!`
|
|
345
345
|
);
|
|
346
346
|
console.log("\n=========================");
|
package/dist/settings/paths.php
CHANGED
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
* @var string SETTINGS_PATH - The absolute path to the settings directory ./settings
|
|
5
5
|
*/
|
|
6
6
|
define("SETTINGS_PATH", dirname(__FILE__));
|
|
7
|
-
/**
|
|
8
|
-
* @var string PUBLIC_PATH - The absolute path to the public directory ./public
|
|
9
|
-
*/
|
|
10
|
-
define("PUBLIC_PATH", dirname(SETTINGS_PATH) . "/public");
|
|
11
7
|
/**
|
|
12
8
|
* @var string PRISMA_LIB_PATH - The absolute path to the Prisma library directory ./src/Lib/Prisma
|
|
13
9
|
*/
|
|
@@ -11,10 +11,10 @@ use Lib\Validator;
|
|
|
11
11
|
*/
|
|
12
12
|
class ChatGPTClient
|
|
13
13
|
{
|
|
14
|
-
private $client;
|
|
15
|
-
private $apiUrl;
|
|
16
|
-
private $apiKey;
|
|
17
|
-
private $cache = [];
|
|
14
|
+
private Client $client;
|
|
15
|
+
private string $apiUrl = '';
|
|
16
|
+
private string $apiKey = '';
|
|
17
|
+
private array $cache = [];
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Constructor initializes the Guzzle HTTP client and sets up API configuration.
|
|
@@ -9,18 +9,19 @@ use DateTime;
|
|
|
9
9
|
use Lib\Validator;
|
|
10
10
|
use GuzzleHttp\Client;
|
|
11
11
|
use GuzzleHttp\Exception\RequestException;
|
|
12
|
+
use Lib\Request;
|
|
12
13
|
|
|
13
14
|
class Auth
|
|
14
15
|
{
|
|
15
16
|
public const PAYLOAD_NAME = 'role';
|
|
16
17
|
public const ROLE_NAME = '';
|
|
17
|
-
public const
|
|
18
|
+
public const PAYLOAD_SESSION_KEY = 'payload_2183A';
|
|
18
19
|
public const COOKIE_NAME = 'pphp_aut_token_D36E5';
|
|
19
20
|
|
|
20
21
|
private static ?Auth $instance = null;
|
|
21
22
|
private const PPHPAUTH = 'pphpauth';
|
|
22
|
-
private $secretKey;
|
|
23
|
-
private $defaultTokenValidity = '1h'; // Default to 1 hour
|
|
23
|
+
private string $secretKey;
|
|
24
|
+
private string $defaultTokenValidity = '1h'; // Default to 1 hour
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Private constructor to prevent direct instantiation.
|
|
@@ -62,16 +63,16 @@ class Auth
|
|
|
62
63
|
* @throws InvalidArgumentException Thrown if the secret key is not set or if the duration format is invalid.
|
|
63
64
|
*
|
|
64
65
|
* Example:
|
|
65
|
-
* $auth =
|
|
66
|
+
* $auth = Auth::getInstance();
|
|
66
67
|
* $auth->setSecretKey('your_secret_key');
|
|
67
68
|
* try {
|
|
68
|
-
* $jwt = $auth->
|
|
69
|
+
* $jwt = $auth->signIn('Admin', '1h');
|
|
69
70
|
* echo "JWT: " . $jwt;
|
|
70
71
|
* } catch (\InvalidArgumentException $e) {
|
|
71
72
|
* echo "Error: " . $e->getMessage();
|
|
72
73
|
* }
|
|
73
74
|
*/
|
|
74
|
-
public function
|
|
75
|
+
public function signIn($data, string $tokenValidity = null): string
|
|
75
76
|
{
|
|
76
77
|
if (!$this->secretKey) {
|
|
77
78
|
throw new \InvalidArgumentException("Secret key is required for authentication.");
|
|
@@ -89,7 +90,7 @@ class Auth
|
|
|
89
90
|
];
|
|
90
91
|
|
|
91
92
|
// Set the payload in the session
|
|
92
|
-
$_SESSION[self::
|
|
93
|
+
$_SESSION[self::PAYLOAD_SESSION_KEY] = $payload;
|
|
93
94
|
|
|
94
95
|
// Encode the JWT
|
|
95
96
|
$jwt = JWT::encode($payload, $this->secretKey, 'HS256');
|
|
@@ -109,15 +110,13 @@ class Auth
|
|
|
109
110
|
*/
|
|
110
111
|
public function isAuthenticated(): bool
|
|
111
112
|
{
|
|
112
|
-
global $_fileToInclude;
|
|
113
|
-
|
|
114
113
|
if (!isset($_COOKIE[self::COOKIE_NAME])) {
|
|
115
|
-
unset($_SESSION[self::
|
|
114
|
+
unset($_SESSION[self::PAYLOAD_SESSION_KEY]);
|
|
116
115
|
return false;
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
if (
|
|
120
|
-
$bearerToken = getBearerToken();
|
|
118
|
+
if (Request::$fileToInclude === 'route.php') {
|
|
119
|
+
$bearerToken = Request::getBearerToken();
|
|
121
120
|
$verifyBearerToken = $this->verifyToken($bearerToken);
|
|
122
121
|
if (!$verifyBearerToken) {
|
|
123
122
|
return false;
|
|
@@ -250,24 +249,24 @@ class Auth
|
|
|
250
249
|
* @param string|null $redirect Optional parameter specifying the URL to redirect to after logging out.
|
|
251
250
|
*
|
|
252
251
|
* Example:
|
|
253
|
-
* $auth =
|
|
254
|
-
* $auth->
|
|
252
|
+
* $auth = Auth::getInstance();
|
|
253
|
+
* $auth->signOut('/login');
|
|
255
254
|
*
|
|
256
255
|
* @return void
|
|
257
256
|
*/
|
|
258
|
-
public function
|
|
257
|
+
public function signOut(string $redirect = null)
|
|
259
258
|
{
|
|
260
259
|
if (isset($_COOKIE[self::COOKIE_NAME])) {
|
|
261
260
|
unset($_COOKIE[self::COOKIE_NAME]);
|
|
262
261
|
setcookie(self::COOKIE_NAME, '', time() - 3600, '/');
|
|
263
262
|
}
|
|
264
263
|
|
|
265
|
-
if (isset($_SESSION[self::
|
|
266
|
-
unset($_SESSION[self::
|
|
264
|
+
if (isset($_SESSION[self::PAYLOAD_SESSION_KEY])) {
|
|
265
|
+
unset($_SESSION[self::PAYLOAD_SESSION_KEY]);
|
|
267
266
|
}
|
|
268
267
|
|
|
269
268
|
if ($redirect) {
|
|
270
|
-
redirect($redirect);
|
|
269
|
+
Request::redirect($redirect);
|
|
271
270
|
}
|
|
272
271
|
}
|
|
273
272
|
|
|
@@ -279,8 +278,8 @@ class Auth
|
|
|
279
278
|
*/
|
|
280
279
|
public function getPayload()
|
|
281
280
|
{
|
|
282
|
-
if (isset($_SESSION[self::
|
|
283
|
-
$value = $_SESSION[self::
|
|
281
|
+
if (isset($_SESSION[self::PAYLOAD_SESSION_KEY])) {
|
|
282
|
+
$value = $_SESSION[self::PAYLOAD_SESSION_KEY][self::PAYLOAD_NAME];
|
|
284
283
|
return is_array($value) ? new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS) : $value;
|
|
285
284
|
}
|
|
286
285
|
|
|
@@ -337,27 +336,27 @@ class Auth
|
|
|
337
336
|
*/
|
|
338
337
|
public function authProviders(...$providers)
|
|
339
338
|
{
|
|
340
|
-
|
|
339
|
+
$dynamicRouteParams = Request::$dynamicParams;
|
|
341
340
|
|
|
342
|
-
if (
|
|
341
|
+
if (Request::$isGet && in_array('signin', $dynamicRouteParams[self::PPHPAUTH])) {
|
|
343
342
|
foreach ($providers as $provider) {
|
|
344
343
|
if ($provider instanceof GithubProvider && in_array('github', $dynamicRouteParams[self::PPHPAUTH])) {
|
|
345
344
|
$githubAuthUrl = "https://github.com/login/oauth/authorize?scope=user:email%20read:user&client_id={$provider->clientId}";
|
|
346
|
-
redirect($githubAuthUrl);
|
|
345
|
+
Request::redirect($githubAuthUrl);
|
|
347
346
|
} elseif ($provider instanceof GoogleProvider && in_array('google', $dynamicRouteParams[self::PPHPAUTH])) {
|
|
348
347
|
$googleAuthUrl = "https://accounts.google.com/o/oauth2/v2/auth?"
|
|
349
348
|
. "scope=" . urlencode('email profile') . "&"
|
|
350
349
|
. "response_type=code&"
|
|
351
350
|
. "client_id=" . urlencode($provider->clientId) . "&"
|
|
352
351
|
. "redirect_uri=" . urlencode($provider->redirectUri);
|
|
353
|
-
redirect($googleAuthUrl);
|
|
352
|
+
Request::redirect($googleAuthUrl);
|
|
354
353
|
}
|
|
355
354
|
}
|
|
356
355
|
}
|
|
357
356
|
|
|
358
357
|
$authCode = Validator::string($_GET['code'] ?? '');
|
|
359
358
|
|
|
360
|
-
if (
|
|
359
|
+
if (Request::$isGet && in_array('callback', $dynamicRouteParams[self::PPHPAUTH]) && isset($authCode)) {
|
|
361
360
|
if (in_array('github', $dynamicRouteParams[self::PPHPAUTH])) {
|
|
362
361
|
$provider = $this->findProvider($providers, GithubProvider::class);
|
|
363
362
|
|
|
@@ -444,7 +443,7 @@ class Auth
|
|
|
444
443
|
];
|
|
445
444
|
$userToAuthenticate = (object)$userToAuthenticate;
|
|
446
445
|
|
|
447
|
-
$this->
|
|
446
|
+
$this->signIn($userToAuthenticate, $githubProvider->maxAge);
|
|
448
447
|
}
|
|
449
448
|
}
|
|
450
449
|
}
|
|
@@ -502,7 +501,7 @@ class Auth
|
|
|
502
501
|
];
|
|
503
502
|
$userToAuthenticate = (object)$userToAuthenticate;
|
|
504
503
|
|
|
505
|
-
$this->
|
|
504
|
+
$this->signIn($userToAuthenticate, $googleProvider->maxAge);
|
|
506
505
|
}
|
|
507
506
|
}
|
|
508
507
|
}
|
|
@@ -4,7 +4,7 @@ namespace Lib\FileManager;
|
|
|
4
4
|
|
|
5
5
|
class UploadFile
|
|
6
6
|
{
|
|
7
|
-
protected string $destination;
|
|
7
|
+
protected string $destination = '';
|
|
8
8
|
protected array $messages = [];
|
|
9
9
|
protected array $errorCode = [];
|
|
10
10
|
protected int $maxSize = 51200; // 50KB default
|
|
@@ -19,7 +19,7 @@ class UploadFile
|
|
|
19
19
|
protected bool $typeCheckingOn = true;
|
|
20
20
|
protected array $notTrusted = ['bin', 'cgi', 'exe', 'js', 'pl', 'php', 'py', 'sh'];
|
|
21
21
|
protected string $suffix = '.upload';
|
|
22
|
-
protected bool $renameDuplicates;
|
|
22
|
+
protected bool $renameDuplicates = true;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Constructor for the UploadFile class.
|
|
@@ -16,21 +16,21 @@ class Boom
|
|
|
16
16
|
*
|
|
17
17
|
* @var int
|
|
18
18
|
*/
|
|
19
|
-
protected int $statusCode;
|
|
19
|
+
protected int $statusCode = 500;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Error message.
|
|
23
23
|
*
|
|
24
24
|
* @var string
|
|
25
25
|
*/
|
|
26
|
-
protected string $errorMessage;
|
|
26
|
+
protected string $errorMessage = 'Internal Server Error';
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Additional error details.
|
|
30
30
|
*
|
|
31
31
|
* @var array
|
|
32
32
|
*/
|
|
33
|
-
protected array $errorDetails;
|
|
33
|
+
protected array $errorDetails = [];
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Boom constructor.
|
|
@@ -4,22 +4,22 @@ namespace Lib;
|
|
|
4
4
|
|
|
5
5
|
class MainLayout
|
|
6
6
|
{
|
|
7
|
-
private static $headScripts = [];
|
|
8
|
-
private static $footerScripts = [];
|
|
9
|
-
|
|
10
|
-
// Default metadata properties
|
|
11
7
|
public static string $title = '';
|
|
12
8
|
public static string $description = '';
|
|
9
|
+
public static string $children = '';
|
|
10
|
+
public static string $childLayoutChildren = '';
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
private static $headScripts = [];
|
|
13
|
+
private static $footerScripts = [];
|
|
15
14
|
private static array $customMetadata = [];
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Adds one or more scripts to the head section if they are not already present.
|
|
19
18
|
*
|
|
20
19
|
* @param string ...$scripts The scripts to be added to the head section.
|
|
20
|
+
* @return void
|
|
21
21
|
*/
|
|
22
|
-
public static function addHeadScript(string ...$scripts)
|
|
22
|
+
public static function addHeadScript(string ...$scripts): void
|
|
23
23
|
{
|
|
24
24
|
foreach ($scripts as $script) {
|
|
25
25
|
if (!in_array($script, self::$headScripts)) {
|
|
@@ -36,8 +36,9 @@ class MainLayout
|
|
|
36
36
|
* it will be appended.
|
|
37
37
|
*
|
|
38
38
|
* @param string ...$scripts One or more scripts to be added to the footer.
|
|
39
|
+
* @return void
|
|
39
40
|
*/
|
|
40
|
-
public static function addFooterScript(string ...$scripts)
|
|
41
|
+
public static function addFooterScript(string ...$scripts): void
|
|
41
42
|
{
|
|
42
43
|
foreach ($scripts as $script) {
|
|
43
44
|
if (!in_array($script, self::$footerScripts)) {
|
|
@@ -51,9 +52,19 @@ class MainLayout
|
|
|
51
52
|
*
|
|
52
53
|
* @return void
|
|
53
54
|
*/
|
|
54
|
-
public static function outputHeadScripts()
|
|
55
|
+
public static function outputHeadScripts(): void
|
|
55
56
|
{
|
|
56
|
-
|
|
57
|
+
$headScriptsWithAttributes = array_map(function ($tag) {
|
|
58
|
+
// Check if the tag is a <link> or <script> and add the dynamic attribute
|
|
59
|
+
if (strpos($tag, '<script') !== false) {
|
|
60
|
+
return str_replace('<script', '<script pp-dynamic-script="81D7D"', $tag);
|
|
61
|
+
} elseif (strpos($tag, '<link') !== false) {
|
|
62
|
+
return str_replace('<link', '<link pp-dynamic-link="81D7D"', $tag);
|
|
63
|
+
}
|
|
64
|
+
return $tag; // Return the tag unchanged if it's neither <script> nor <link>
|
|
65
|
+
}, self::$headScripts);
|
|
66
|
+
|
|
67
|
+
echo implode("\n", $headScriptsWithAttributes);
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
/**
|
|
@@ -61,7 +72,7 @@ class MainLayout
|
|
|
61
72
|
*
|
|
62
73
|
* @return void
|
|
63
74
|
*/
|
|
64
|
-
public static function outputFooterScripts()
|
|
75
|
+
public static function outputFooterScripts(): void
|
|
65
76
|
{
|
|
66
77
|
echo implode("\n", self::$footerScripts);
|
|
67
78
|
}
|
|
@@ -71,7 +82,7 @@ class MainLayout
|
|
|
71
82
|
*
|
|
72
83
|
* @return void
|
|
73
84
|
*/
|
|
74
|
-
public static function clearHeadScripts()
|
|
85
|
+
public static function clearHeadScripts(): void
|
|
75
86
|
{
|
|
76
87
|
self::$headScripts = [];
|
|
77
88
|
}
|
|
@@ -81,7 +92,7 @@ class MainLayout
|
|
|
81
92
|
*
|
|
82
93
|
* @return void
|
|
83
94
|
*/
|
|
84
|
-
public static function clearFooterScripts()
|
|
95
|
+
public static function clearFooterScripts(): void
|
|
85
96
|
{
|
|
86
97
|
self::$footerScripts = [];
|
|
87
98
|
}
|
|
@@ -93,7 +104,7 @@ class MainLayout
|
|
|
93
104
|
* @param string $value
|
|
94
105
|
* @return void
|
|
95
106
|
*/
|
|
96
|
-
public static function addCustomMetadata(string $key, string $value)
|
|
107
|
+
public static function addCustomMetadata(string $key, string $value): void
|
|
97
108
|
{
|
|
98
109
|
self::$customMetadata[$key] = $value;
|
|
99
110
|
}
|
|
@@ -114,15 +125,24 @@ class MainLayout
|
|
|
114
125
|
*
|
|
115
126
|
* @return void
|
|
116
127
|
*/
|
|
117
|
-
public static function outputMetadata()
|
|
128
|
+
public static function outputMetadata(): void
|
|
118
129
|
{
|
|
119
130
|
// Output standard metadata
|
|
120
131
|
echo '<title>' . htmlspecialchars(self::$title) . '</title>' . "\n";
|
|
121
|
-
echo '<meta name="description" content="' . htmlspecialchars(self::$description) . '">' . "\n";
|
|
122
132
|
|
|
123
|
-
//
|
|
133
|
+
// Ensure the description is included in custom metadata if not already present
|
|
134
|
+
if (!isset(self::$customMetadata['description'])) {
|
|
135
|
+
self::$customMetadata['description'] = self::$description;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
$customMetadataContent = [];
|
|
124
139
|
foreach (self::$customMetadata as $key => $value) {
|
|
125
|
-
|
|
140
|
+
// Add the dynamic meta ID attribute to each <meta> tag
|
|
141
|
+
$customMetadataContent[] = '<meta name="' . htmlspecialchars($key) . '" content="' . htmlspecialchars($value) . '" pp-dynamic-meta="81D7D">';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!empty($customMetadataContent)) {
|
|
145
|
+
echo implode("\n", $customMetadataContent) . "\n";
|
|
126
146
|
}
|
|
127
147
|
}
|
|
128
148
|
|
|
@@ -131,7 +151,7 @@ class MainLayout
|
|
|
131
151
|
*
|
|
132
152
|
* @return void
|
|
133
153
|
*/
|
|
134
|
-
public static function clearCustomMetadata()
|
|
154
|
+
public static function clearCustomMetadata(): void
|
|
135
155
|
{
|
|
136
156
|
self::$customMetadata = [];
|
|
137
157
|
}
|
|
@@ -4,31 +4,32 @@ namespace Lib\Middleware;
|
|
|
4
4
|
|
|
5
5
|
use Lib\Auth\Auth;
|
|
6
6
|
use Lib\Auth\AuthConfig;
|
|
7
|
+
use Lib\Request;
|
|
7
8
|
|
|
8
9
|
final class AuthMiddleware
|
|
9
10
|
{
|
|
10
|
-
public static function handle($
|
|
11
|
+
public static function handle($requestPathname)
|
|
11
12
|
{
|
|
12
|
-
$
|
|
13
|
-
if (!self::matches($
|
|
13
|
+
$requestPathname = trim($requestPathname);
|
|
14
|
+
if (!self::matches($requestPathname)) {
|
|
14
15
|
return;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
// Check if the user is authorized to access the route or redirect to login
|
|
18
19
|
if (!self::isAuthorized()) {
|
|
19
|
-
redirect('/auth/login');
|
|
20
|
+
Request::redirect('/auth/login');
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
// Check if the user has the required role to access the route or redirect to denied
|
|
23
|
-
if (AuthConfig::IS_ROLE_BASE && !self::hasRequiredRole($
|
|
24
|
-
redirect('/denied');
|
|
24
|
+
if (AuthConfig::IS_ROLE_BASE && !self::hasRequiredRole($requestPathname)) {
|
|
25
|
+
Request::redirect('/denied');
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
protected static function matches($
|
|
29
|
+
protected static function matches($requestPathname)
|
|
29
30
|
{
|
|
30
31
|
foreach (AuthConfig::$privateRoutes ?? [] as $pattern) {
|
|
31
|
-
if (self::getUriRegex($pattern, $
|
|
32
|
+
if (self::getUriRegex($pattern, $requestPathname)) {
|
|
32
33
|
return true;
|
|
33
34
|
}
|
|
34
35
|
}
|
|
@@ -40,7 +41,7 @@ final class AuthMiddleware
|
|
|
40
41
|
$auth = Auth::getInstance();
|
|
41
42
|
$cookieName = Auth::COOKIE_NAME;
|
|
42
43
|
if (!isset($_COOKIE[$cookieName])) {
|
|
43
|
-
unset($_SESSION[Auth::
|
|
44
|
+
unset($_SESSION[Auth::PAYLOAD_SESSION_KEY]);
|
|
44
45
|
return false;
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -64,12 +65,12 @@ final class AuthMiddleware
|
|
|
64
65
|
return false;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
protected static function hasRequiredRole($
|
|
68
|
+
protected static function hasRequiredRole($requestPathname): bool
|
|
68
69
|
{
|
|
69
70
|
$auth = Auth::getInstance();
|
|
70
71
|
$roleBasedRoutes = AuthConfig::$roleBasedRoutes ?? [];
|
|
71
72
|
foreach ($roleBasedRoutes as $pattern => $data) {
|
|
72
|
-
if (self::getUriRegex($pattern, $
|
|
73
|
+
if (self::getUriRegex($pattern, $requestPathname)) {
|
|
73
74
|
$userRole = Auth::ROLE_NAME ? $auth->getPayload()[Auth::ROLE_NAME] : $auth->getPayload();
|
|
74
75
|
if ($userRole !== null && AuthConfig::checkAuthRole($userRole, $data[AuthConfig::ROLE_IDENTIFIER])) {
|
|
75
76
|
return true;
|
|
@@ -79,19 +80,19 @@ final class AuthMiddleware
|
|
|
79
80
|
return false;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
private static function getUriRegex($pattern, $
|
|
83
|
+
private static function getUriRegex($pattern, $requestPathname)
|
|
83
84
|
{
|
|
84
85
|
$pattern = strtolower($pattern);
|
|
85
|
-
$
|
|
86
|
+
$requestPathname = strtolower(trim($requestPathname));
|
|
86
87
|
|
|
87
|
-
// Handle the case where the
|
|
88
|
-
if (empty($
|
|
89
|
-
$
|
|
88
|
+
// Handle the case where the requestPathname is empty, which means home or "/"
|
|
89
|
+
if (empty($requestPathname) || $requestPathname === '/') {
|
|
90
|
+
$requestPathname = '/';
|
|
90
91
|
} else {
|
|
91
|
-
$
|
|
92
|
+
$requestPathname = "/" . $requestPathname;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
$regex = "#^/?" . preg_quote($pattern, '#') . "(/.*)?$#";
|
|
95
|
-
return preg_match($regex, $
|
|
96
|
+
return preg_match($regex, $requestPathname);
|
|
96
97
|
}
|
|
97
98
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Lib;
|
|
4
|
+
|
|
5
|
+
class PrismaPHPSettings
|
|
6
|
+
{
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
public static \ArrayObject $option;
|
|
11
|
+
|
|
12
|
+
public static array $routeFiles = [];
|
|
13
|
+
|
|
14
|
+
public static function init(): void
|
|
15
|
+
{
|
|
16
|
+
self::$option = self::getPrismaSettings();
|
|
17
|
+
self::$routeFiles = self::getRoutesFileList();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private static function getPrismaSettings(): \ArrayObject
|
|
21
|
+
{
|
|
22
|
+
$prismaPHPSettingsJson = DOCUMENT_PATH . '/prisma-php.json';
|
|
23
|
+
|
|
24
|
+
if (file_exists($prismaPHPSettingsJson)) {
|
|
25
|
+
$jsonContent = file_get_contents($prismaPHPSettingsJson);
|
|
26
|
+
$decodedJson = json_decode($jsonContent, true);
|
|
27
|
+
|
|
28
|
+
if (json_last_error() === JSON_ERROR_NONE) {
|
|
29
|
+
return new \ArrayObject($decodedJson, \ArrayObject::ARRAY_AS_PROPS);
|
|
30
|
+
} else {
|
|
31
|
+
return new \ArrayObject([]);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private static function getRoutesFileList(): array
|
|
37
|
+
{
|
|
38
|
+
$jsonFileName = SETTINGS_PATH . '/files-list.json';
|
|
39
|
+
$routeFiles = file_exists($jsonFileName) ? json_decode(file_get_contents($jsonFileName), true) : [];
|
|
40
|
+
|
|
41
|
+
return $routeFiles;
|
|
42
|
+
}
|
|
43
|
+
}
|