create-prisma-php-app 1.8.20 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.htaccess +12 -1
- package/dist/bootstrap-ajax.php +141 -0
- package/dist/bootstrap.php +2 -12
- package/dist/index.js +1 -1
- package/dist/prisma-client-php/index.js +13 -13
- package/dist/settings/project-name.cjs +56 -0
- package/dist/src/app/js/index.js +192 -63
- package/package.json +1 -1
- package/dist/settings/project-settings.js +0 -6
- package/dist/src/app/api/api.php +0 -98
package/dist/.htaccess
CHANGED
|
@@ -10,9 +10,20 @@ RewriteEngine On
|
|
|
10
10
|
# Allow cross-origin requests (CORS) for all routes
|
|
11
11
|
<IfModule mod_headers.c>
|
|
12
12
|
Header set Access-Control-Allow-Origin "*"
|
|
13
|
-
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
|
|
13
|
+
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS"
|
|
14
14
|
Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
|
|
15
15
|
</IfModule>
|
|
16
16
|
|
|
17
|
+
# Exclude OPTIONS method from being redirected
|
|
18
|
+
RewriteCond %{REQUEST_METHOD} !OPTIONS
|
|
19
|
+
|
|
20
|
+
# Redirect all AJAX requests to bootstrap-ajax.php
|
|
21
|
+
RewriteCond %{HTTP:X-Requested-With} ^XMLHttpRequest$
|
|
22
|
+
RewriteRule ^(.*)$ bootstrap-ajax.php [L,QSA]
|
|
23
|
+
|
|
17
24
|
# Redirect all non-file and non-directory requests not starting with src/app/ to src/app/layout.php
|
|
18
25
|
RewriteRule !^src/app/ src/app/layout.php [QSA,L]
|
|
26
|
+
|
|
27
|
+
# Add this to ensure OPTIONS requests are handled correctly
|
|
28
|
+
RewriteCond %{REQUEST_METHOD} OPTIONS
|
|
29
|
+
RewriteRule ^(.*)$ $1 [R=200,L]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
header('Content-Type: application/json');
|
|
4
|
+
|
|
5
|
+
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
|
|
6
|
+
header('HTTP/1.1 200 OK');
|
|
7
|
+
exit;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (empty($_SERVER["HTTP_X_REQUESTED_WITH"]) || $_SERVER["HTTP_X_REQUESTED_WITH"] != "XMLHttpRequest") {
|
|
11
|
+
header("HTTP/1.0 400 Bad Request");
|
|
12
|
+
echo json_encode(["error" => "Invalid request method or type."]);
|
|
13
|
+
exit;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Determine the request method
|
|
17
|
+
$requestMethod = $_SERVER['REQUEST_METHOD'];
|
|
18
|
+
$allowedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
|
19
|
+
|
|
20
|
+
if (!in_array($requestMethod, $allowedMethods)) {
|
|
21
|
+
echo "Method not allowed\n";
|
|
22
|
+
header("HTTP/1.1 405 Method Not Allowed");
|
|
23
|
+
exit;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
$isGet = $requestMethod === 'GET';
|
|
27
|
+
$isPost = $requestMethod === 'POST';
|
|
28
|
+
$isPut = $requestMethod === 'PUT';
|
|
29
|
+
$isDelete = $requestMethod === 'DELETE';
|
|
30
|
+
$isHead = $requestMethod === 'HEAD';
|
|
31
|
+
$isOptions = $requestMethod === 'OPTIONS';
|
|
32
|
+
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
|
33
|
+
$params = [];
|
|
34
|
+
|
|
35
|
+
if (stripos($contentType, 'application/json') !== false) {
|
|
36
|
+
$jsonInput = file_get_contents('php://input');
|
|
37
|
+
if (!empty($jsonInput)) {
|
|
38
|
+
$data = json_decode($jsonInput, true);
|
|
39
|
+
if (json_last_error() === JSON_ERROR_NONE) {
|
|
40
|
+
$params = $data;
|
|
41
|
+
} else {
|
|
42
|
+
echo json_encode(['error' => 'Error: Invalid JSON body!']);
|
|
43
|
+
exit;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
$scriptUrl = $_SERVER['REQUEST_URI'];
|
|
49
|
+
$scriptUrl = explode('?', $scriptUrl, 2)[0];
|
|
50
|
+
$uri = $_SERVER['SCRIPT_URL'] ?? uriExtractor($scriptUrl);
|
|
51
|
+
|
|
52
|
+
// Check for private directory access
|
|
53
|
+
if (strpos($uri, '/_') !== false || strpos($uri, '_') === 0) {
|
|
54
|
+
handleNotFound();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
$baseDir = __DIR__;
|
|
58
|
+
|
|
59
|
+
require_once $baseDir . "/settings/paths.php";
|
|
60
|
+
require_once $baseDir . "/vendor/autoload.php";
|
|
61
|
+
|
|
62
|
+
$groupFolder = findGroupFolder($uri);
|
|
63
|
+
$filePath = $baseDir . '/src/app' . $groupFolder;
|
|
64
|
+
|
|
65
|
+
if (basename($filePath) === 'route.php' && file_exists($filePath)) {
|
|
66
|
+
require_once($filePath);
|
|
67
|
+
} else {
|
|
68
|
+
handleNotFound();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function uriExtractor(string $scriptUrl): string
|
|
72
|
+
{
|
|
73
|
+
$prismaPHPSettings = json_decode(file_get_contents("prisma-php.json"), true);
|
|
74
|
+
$projectName = $prismaPHPSettings['projectName'] ?? '';
|
|
75
|
+
if (empty($projectName)) {
|
|
76
|
+
return "/";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
$escapedIdentifier = preg_quote($projectName, '/');
|
|
80
|
+
$pattern = "/(?:{$escapedIdentifier})(\/.+?)(?=\/{$escapedIdentifier}\/|$)/";
|
|
81
|
+
|
|
82
|
+
if (preg_match_all($pattern, $scriptUrl, $matches, PREG_SET_ORDER)) {
|
|
83
|
+
$lastMatch = end($matches);
|
|
84
|
+
if (!empty($lastMatch[1])) {
|
|
85
|
+
return $lastMatch[1];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return "/";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function handleNotFound()
|
|
93
|
+
{
|
|
94
|
+
header("HTTP/1.0 404 Not Found");
|
|
95
|
+
echo json_encode(['error' => 'Not Found', 'message' => 'The requested resource was not found.']);
|
|
96
|
+
exit;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function findGroupFolder($uri): string
|
|
100
|
+
{
|
|
101
|
+
$uriSegments = explode('/', $uri);
|
|
102
|
+
foreach ($uriSegments as $segment) {
|
|
103
|
+
if (!empty($segment)) {
|
|
104
|
+
if (isGroupIdentifier($segment)) {
|
|
105
|
+
return $segment;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
$matchedGroupFolder = matchGroupFolder($uri);
|
|
111
|
+
if ($matchedGroupFolder) {
|
|
112
|
+
return $matchedGroupFolder;
|
|
113
|
+
} else {
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isGroupIdentifier($segment): bool
|
|
119
|
+
{
|
|
120
|
+
return preg_match('/^\(.*\)$/', $segment);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function matchGroupFolder($constructedPath): ?string
|
|
124
|
+
{
|
|
125
|
+
$routes = json_decode(file_get_contents(SETTINGS_PATH . "/files-list.json"), true);
|
|
126
|
+
$bestMatch = null;
|
|
127
|
+
$normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
|
|
128
|
+
|
|
129
|
+
$normalizedConstructedPath = "/$normalizedConstructedPath/route.php";
|
|
130
|
+
|
|
131
|
+
foreach ($routes as $route) {
|
|
132
|
+
$normalizedRoute = trim(str_replace('\\', '/', $route), '.');
|
|
133
|
+
$cleanedRoute = preg_replace('/\/\([^)]+\)/', '', $normalizedRoute);
|
|
134
|
+
if ($cleanedRoute === $normalizedConstructedPath) {
|
|
135
|
+
$bestMatch = $normalizedRoute;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return $bestMatch;
|
|
141
|
+
}
|
package/dist/bootstrap.php
CHANGED
|
@@ -7,7 +7,7 @@ if (session_status() == PHP_SESSION_NONE) {
|
|
|
7
7
|
require_once __DIR__ . '/settings/paths.php';
|
|
8
8
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
9
9
|
|
|
10
|
-
$metadata =
|
|
10
|
+
$metadata = require_once __DIR__ . '/src/app/metadata.php';
|
|
11
11
|
|
|
12
12
|
function determineContentToInclude()
|
|
13
13
|
{
|
|
@@ -40,13 +40,6 @@ function determineContentToInclude()
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
if (substr($uri, -4) == '.php') {
|
|
44
|
-
$path = $baseDir . '/' . $uri;
|
|
45
|
-
if (file_exists($path)) {
|
|
46
|
-
$includePath = $path;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
43
|
$currentPath = $baseDir;
|
|
51
44
|
$getGroupFolder = getGroupFolder($groupFolder);
|
|
52
45
|
$modifiedUri = $uri;
|
|
@@ -153,10 +146,7 @@ function matchGroupFolder($constructedPath): ?string
|
|
|
153
146
|
$routes = json_decode(file_get_contents(SETTINGS_PATH . "/files-list.json"), true);
|
|
154
147
|
$bestMatch = null;
|
|
155
148
|
$normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
|
|
156
|
-
|
|
157
|
-
if (substr($normalizedConstructedPath, -4) !== '.php') {
|
|
158
|
-
$normalizedConstructedPath = "/$normalizedConstructedPath/index.php";
|
|
159
|
-
}
|
|
149
|
+
$normalizedConstructedPath = "/$normalizedConstructedPath/index.php";
|
|
160
150
|
|
|
161
151
|
foreach ($routes as $route) {
|
|
162
152
|
$normalizedRoute = trim(str_replace('\\', '/', $route), '.');
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
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;function configureBrowserSyncCommand(e,s){const t=s.PROJECT_ROOT_PATH.indexOf("\\htdocs\\");if(-1===t)return"";const n=s.PROJECT_ROOT_PATH.substring(0,t+"\\htdocs\\".length).replace(/\\/g,"\\\\"),i=s.PROJECT_ROOT_PATH.replace(new RegExp(`^${n}`),"").replace(/\\/g,"/");let c=`http://localhost/${i}`;c=c.endsWith("/")?c.slice(0,-1):c;const r=c.replace(/(?<!:)(\/\/+)/g,"/"),o=i.replace(/\/\/+/g,"/"),a=`\n const { createProxyMiddleware } = require("http-proxy-middleware");\n\n module.exports = {\n // First middleware: Set Cache-Control headers\n function (req, res, next) {\n res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");\n res.setHeader("Pragma", "no-cache");\n res.setHeader("Expires", "0");\n next();\n },\n // Use the 'middleware' option to create a proxy that masks the deep URL.\n middleware: [\n // This middleware intercepts requests to the root and proxies them to the deep path.\n createProxyMiddleware("/", {\n target:\n "${r}/",\n changeOrigin: true,\n pathRewrite: {\n "^/": "/${o.startsWith("/")?o.substring(1):o}/", // Rewrite the path.\n },\n }),\n ],\n proxy: "http://localhost:3000", // Proxy the BrowserSync server.\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n };`,l=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(l,a,"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s,t){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const i=JSON.parse(fs.readFileSync(n,"utf8")),c=configureBrowserSyncCommand(e,s);i.scripts=Object.assign(Object.assign({},i.scripts),{postinstall:"prisma generate"});let r=[];t.tailwindcss&&(i.scripts=Object.assign(Object.assign({},i.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),r.push("tailwind")),t.websocket&&(i.scripts=Object.assign(Object.assign({},i.scripts),{websocket:"node ./settings/restartWebsocket.cjs"}),r.push("websocket"));const o=Object.assign({},i.scripts);r.length>0&&(o["browser-sync"]=c),o.dev=r.length>0?`npm-run-all --parallel browser-sync ${r.join(" ")}`:c,i.scripts=o,i.type="module",i.prisma={seed:"node prisma/seed.js"},fs.writeFileSync(n,JSON.stringify(i,null,2))}async function updateComposerJson(e,s){if(!s.websocket)return;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=Object.assign(Object.assign({},n.require),{"cboden/ratchet":"^0.4.4"})),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\nconst 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="";fs.existsSync(t)&&(n=fs.readFileSync(t,"utf8")),s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s){const t=fs.existsSync(e),n=t&&fs.statSync(e);if(t&&n&&n.isDirectory())fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t))}));else{if(checkExcludeFiles(s))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s){s.forEach((({srcDir:s,destDir:t})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,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/app/**/*.{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");const n='\n <link href="<?php echo $baseUrl; ?>css/index.css" rel="stylesheet">\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">\n <script src="<?php echo $baseUrl; ?>js/index.js"><\/script>',i=s?` <link href="<?php echo $baseUrl; ?>css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`;e=e.replace("</head>",`${i}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");if(checkExcludeFiles(t))return;let n=fs.existsSync(t)?fs.readFileSync(t,"utf8"):"";n.includes(s)||(n+=`${""!==n?"\n\n":""}${s}`,fs.writeFileSync(t,n,{flag:"w"}))}function checkExcludeFiles(e){var s,t;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(t=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==t&&t)}async function createDirectoryStructure(e,s,t){const n=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(n.push({src:"/.env",dest:"/.env"},{src:"/tsconfig.json",dest:"/tsconfig.json"}),updateAnswer.tailwindcss&&n.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"}));n.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),i=path.join(e,t);if(checkExcludeFiles(i))return;const c=fs.readFileSync(n,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/prisma",destDir:"/prisma"},{srcDir:"/src",destDir:"/src"}]),await updatePackageJson(e,t,s),await updateComposerJson(e,s),await updateIndexJsForWebSocket(e,s),s.tailwindcss?(createOrUpdateTailwindConfig(e),modifyLayoutPHP(e,!0),modifyPostcssConfig(e)):modifyLayoutPHP(e,!1);await createOrUpdateEnvFile(e,'# 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"'),await createUpdateGitignoreFile(e,["vendor"])}async function getAnswer(e={}){var s,t,n;const i=[];e.projectName||i.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.tailwindcss||i.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||i.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"});const c=i,r=()=>{process.exit(0)};try{const i=await prompts(c,{onCancel:r});return 0===Object.keys(i).length?null:{projectName:i.projectName?String(i.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",tailwindcss:null!==(t=i.tailwindcss)&&void 0!==t?t:e.tailwindcss,websocket:null!==(n=i.websocket)&&void 0!==n?n:e.websocket}}catch(e){return null}}async function installDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}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)};async function main(){var e,s,t,n,i;try{const c=process.argv.slice(2);let r=c[0],o=null;if(r){const i={projectName:r,tailwindcss:c.includes("--tailwindcss"),websocket:c.includes("--websocket")};if(o=await getAnswer(i),null===o)return;const a=process.cwd(),l=path.join(a,"prisma-php.json"),p=readJsonFile(l);let d=[];null===(e=p.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(a,e);fs.existsSync(s)&&d.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:r,tailwindcss:null!==(s=null==o?void 0:o.tailwindcss)&&void 0!==s&&s,websocket:null!==(t=null==o?void 0:o.websocket)&&void 0!==t&&t,isUpdate:!0,excludeFiles:null!==(n=p.excludeFiles)&&void 0!==n?n:[],excludeFilePath:null!=d?d:[],filePath:a}}else o=await getAnswer();if(null===o)return;execSync("npm install -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"}),r||fs.mkdirSync(o.projectName);const a=process.cwd();let l=r?a:path.join(a,o.projectName);r||process.chdir(o.projectName);const p=["prisma","@prisma/client","typescript","@types/node","ts-node","http-proxy-middleware"];o.tailwindcss&&p.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),o.websocket&&p.push("chokidar-cli"),(o.tailwindcss||o.websocket)&&p.push("npm-run-all"),await installDependencies(l,p,!0),r||(execSync("npx prisma init",{stdio:"inherit"}),execSync("npx tsc --init",{stdio:"inherit"})),o.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"});const d={PROJECT_NAME:o.projectName,PROJECT_ROOT_PATH:l.replace(/\\/g,"\\\\"),PHP_ROOT_PATH_EXE:"D:\\\\xampp\\\\php\\\\php.exe",PHP_GENERATE_CLASS_PATH:"src/Lib/Prisma/Classes"};await createDirectoryStructure(l,o,d);const u=path.join(l,"settings","project-settings.js"),f=`export const projectSettings = {\n PROJECT_NAME: "${o.projectName}",\n PROJECT_ROOT_PATH: "${l.replace(/\\/g,"\\\\")}",\n PHP_ROOT_PATH_EXE: "D:\\\\xampp\\\\php\\\\php.exe",\n PHP_GENERATE_CLASS_PATH: "src/Lib/Prisma/Classes",\n };`;fs.writeFileSync(u,f,{flag:"w"});const h=path.join(l,"public");if(fs.existsSync(h)||fs.mkdirSync(h),!o.tailwindcss){const e=path.join(l,"src","app","css");["tailwind.css","styles.css"].forEach((s=>{const t=path.join(e,s);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(!o.websocket){const e=path.join(l,"src","lib","websocket");fs.existsSync(e)&&fs.rmSync(e,{recursive:!0,force:!0});const s=path.join(l,"settings");["restartWebsocket.cjs","restart_websocket.bat"].forEach((e=>{const t=path.join(s,e);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(l,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}updateAnswer.websocket||e.push("chokidar-cli"),e.length>0&&await uninstallDependencies(l,e,!0)}const w=await fetchPackageVersion("create-prisma-php-app"),y={projectName:o.projectName,tailwindcss:o.tailwindcss,websocket:o.websocket,version:w,excludeFiles:null!==(i=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==i?i:[]};fs.writeFileSync(path.join(l,"prisma-php.json"),JSON.stringify(y,null,2),{flag:"w"})}catch(e){process.exit(1)}}main();
|
|
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;function bsConfigUrls(e){const s=e.PROJECT_ROOT_PATH.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.PROJECT_ROOT_PATH.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.PROJECT_ROOT_PATH.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let i=`http://localhost/${n}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),r=n.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}function configureBrowserSyncCommand(e,s){const t=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(t,'const { createProxyMiddleware } = require("http-proxy-middleware");\n const fs = require("fs");\n \n const jsonData = fs.readFileSync("prisma-php.json", "utf8");\n const config = JSON.parse(jsonData);\n \n module.exports = {\n // First middleware: Set Cache-Control headers\n function(req, res, next) {\n res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");\n res.setHeader("Pragma", "no-cache");\n res.setHeader("Expires", "0");\n next();\n },\n // Use the \'middleware\' option to create a proxy that masks the deep URL.\n middleware: [\n // This middleware intercepts requests to the root and proxies them to the deep path.\n createProxyMiddleware("/", {\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n proxy: "http://localhost:3000", // Proxy the BrowserSync server.\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n };',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s,t){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const i=JSON.parse(fs.readFileSync(n,"utf8")),c=configureBrowserSyncCommand(e,s);i.scripts=Object.assign(Object.assign({},i.scripts),{postinstall:"prisma generate",projectName:"node settings/project-name.cjs"});let r=[];t.tailwindcss&&(i.scripts=Object.assign(Object.assign({},i.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),r.push("tailwind")),t.websocket&&(i.scripts=Object.assign(Object.assign({},i.scripts),{websocket:"node ./settings/restartWebsocket.cjs"}),r.push("websocket"));const o=Object.assign({},i.scripts);r.length>0&&(o["browser-sync"]=c),o.dev=r.length>0?`npm-run-all --parallel projectName browser-sync ${r.join(" ")}`:`npm run projectName && ${c}`,i.scripts=o,i.type="module",i.prisma={seed:"node prisma/seed.js"},fs.writeFileSync(n,JSON.stringify(i,null,2))}async function updateComposerJson(e,s){if(!s.websocket)return;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=Object.assign(Object.assign({},n.require),{"cboden/ratchet":"^0.4.4"})),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\nconst 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="";fs.existsSync(t)&&(n=fs.readFileSync(t,"utf8")),s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s){const t=fs.existsSync(e),n=t&&fs.statSync(e);if(t&&n&&n.isDirectory())fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t))}));else{if(checkExcludeFiles(s))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s){s.forEach((({srcDir:s,destDir:t})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,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/app/**/*.{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");const n='\n <link href="<?php echo $baseUrl; ?>css/index.css" rel="stylesheet">\n <script src="<?php echo $baseUrl; ?>js/index.js"><\/script>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">',i=s?` <link href="<?php echo $baseUrl; ?>css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`;e=e.replace("</head>",`${i}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");if(checkExcludeFiles(t))return;let n=fs.existsSync(t)?fs.readFileSync(t,"utf8"):"";n.includes(s)||(n+=`${""!==n?"\n\n":""}${s}`,fs.writeFileSync(t,n,{flag:"w"}))}function checkExcludeFiles(e){var s,t;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(t=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==t&&t)}async function createDirectoryStructure(e,s,t){const n=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(n.push({src:"/.env",dest:"/.env"},{src:"/tsconfig.json",dest:"/tsconfig.json"}),updateAnswer.tailwindcss&&n.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"}));n.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),i=path.join(e,t);if(checkExcludeFiles(i))return;const c=fs.readFileSync(n,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/prisma",destDir:"/prisma"},{srcDir:"/src",destDir:"/src"}]),await updatePackageJson(e,t,s),await updateComposerJson(e,s),await updateIndexJsForWebSocket(e,s),s.tailwindcss?(createOrUpdateTailwindConfig(e),modifyLayoutPHP(e,!0),modifyPostcssConfig(e)):modifyLayoutPHP(e,!1);await createOrUpdateEnvFile(e,'# 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"'),await createUpdateGitignoreFile(e,["vendor"])}async function getAnswer(e={}){var s,t,n;const i=[];e.projectName||i.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.tailwindcss||i.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||i.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"});const c=i,r=()=>{process.exit(0)};try{const i=await prompts(c,{onCancel:r});return 0===Object.keys(i).length?null:{projectName:i.projectName?String(i.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",tailwindcss:null!==(t=i.tailwindcss)&&void 0!==t?t:e.tailwindcss,websocket:null!==(n=i.websocket)&&void 0!==n?n:e.websocket}}catch(e){return null}}async function installDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}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)};async function main(){var e,s,t,n,i;try{const c=process.argv.slice(2);let r=c[0],o=null;if(r){const i={projectName:r,tailwindcss:c.includes("--tailwindcss"),websocket:c.includes("--websocket")};if(o=await getAnswer(i),null===o)return;const a=process.cwd(),l=path.join(a,"prisma-php.json"),p=readJsonFile(l);let d=[];null===(e=p.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(a,e);fs.existsSync(s)&&d.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:r,tailwindcss:null!==(s=null==o?void 0:o.tailwindcss)&&void 0!==s&&s,websocket:null!==(t=null==o?void 0:o.websocket)&&void 0!==t&&t,isUpdate:!0,excludeFiles:null!==(n=p.excludeFiles)&&void 0!==n?n:[],excludeFilePath:null!=d?d:[],filePath:a}}else o=await getAnswer();if(null===o)return;execSync("npm install -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"}),r||fs.mkdirSync(o.projectName);const a=process.cwd();let l=r?a:path.join(a,o.projectName);r||process.chdir(o.projectName);const p=["prisma","@prisma/client","typescript","@types/node","ts-node","http-proxy-middleware@^2.0.6"];o.tailwindcss&&p.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),o.websocket&&p.push("chokidar-cli"),(o.tailwindcss||o.websocket)&&p.push("npm-run-all"),await installDependencies(l,p,!0),r||(execSync("npx prisma init",{stdio:"inherit"}),execSync("npx tsc --init",{stdio:"inherit"})),o.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"});const d=l.replace(/\\/g,"\\"),u={PROJECT_NAME:o.projectName,PROJECT_ROOT_PATH:d,PHP_ROOT_PATH_EXE:"D:\\\\xampp\\\\php\\\\php.exe",PHP_GENERATE_CLASS_PATH:"src/Lib/Prisma/Classes"};await createDirectoryStructure(l,o,u);const f=path.join(l,"public");if(fs.existsSync(f)||fs.mkdirSync(f),!o.tailwindcss){const e=path.join(l,"src","app","css");["tailwind.css","styles.css"].forEach((s=>{const t=path.join(e,s);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(!o.websocket){const e=path.join(l,"src","lib","websocket");fs.existsSync(e)&&fs.rmSync(e,{recursive:!0,force:!0});const s=path.join(l,"settings");["restartWebsocket.cjs","restart_websocket.bat"].forEach((e=>{const t=path.join(s,e);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(l,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}updateAnswer.websocket||e.push("chokidar-cli"),e.length>0&&await uninstallDependencies(l,e,!0)}const h=await fetchPackageVersion("create-prisma-php-app"),w=bsConfigUrls(u),y={projectName:o.projectName,projectRootPath:d,phpRootPathExe:"D:\\xampp\\php\\php.exe",phpGenerateClassPath:"src/Lib/Prisma/Classes",bsTarget:w.bsTarget,bsPathRewrite:w.bsPathRewrite,tailwindcss:o.tailwindcss,websocket:o.websocket,version:h,excludeFiles:null!==(i=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==i?i:[]};fs.writeFileSync(path.join(l,"prisma-php.json"),JSON.stringify(y,null,2),{flag:"w"})}catch(e){process.exit(1)}}main();
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import*as fs from"fs";import{exec}from"child_process";import path from"path";import{fileURLToPath
|
|
1
|
+
import*as fs from"fs";import{exec}from"child_process";import path from"path";import{fileURLToPath}from"url";import CryptoJS from"crypto-js";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),getSecretKey=()=>{const r=fs.readFileSync(`${__dirname}/key.enc`,"utf8");if(r.length<400)throw new Error("File content is less than 400 characters.");return r.substring(247,289)},decryptData=(r,t)=>CryptoJS.AES.decrypt(r,t).toString(CryptoJS.enc.Utf8);
|
|
2
2
|
const executePHP = (command) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
exec(command, (error, stdout, stderr) => {
|
|
4
|
+
if (error) {
|
|
5
|
+
console.error(`Execution error: ${error}`);
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (stderr) {
|
|
9
|
+
console.error(`Standard error: ${stderr}`);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log(`Standard output...\n${stdout}`);
|
|
13
|
+
});
|
|
14
14
|
};
|
|
15
|
-
const main=async()=>{try{const e=process.cwd(),
|
|
15
|
+
const main=async()=>{try{const e=process.cwd(),n=path.join(e,"prisma-php.json"),a=fs.readFileSync(n,{encoding:"utf8"}),c=JSON.parse(a),t=c.phpGenerateClassPath,i=`${__dirname}/index.php`,p=`${__dirname}/index.enc`,s=getSecretKey(),r=fs.readFileSync(p,{encoding:"utf8"}),d=decryptData(r,s);fs.writeFileSync(`${__dirname}/index.php`,d);const h=`${c.phpRootPathExe} ${i} ${t}`;executePHP(h)}catch(e){}};main().catch((e=>{}));
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
// Path to your JSON configuration file
|
|
5
|
+
const configFilePath = path.join(__dirname, "..", "prisma-php.json");
|
|
6
|
+
|
|
7
|
+
// Use the parent directory name as the new project name
|
|
8
|
+
const newProjectName = path.basename(path.join(__dirname, ".."));
|
|
9
|
+
|
|
10
|
+
// Function to update the project name and paths in the JSON config
|
|
11
|
+
function updateProjectNameInConfig(filePath, newProjectName) {
|
|
12
|
+
fs.readFile(filePath, "utf8", (err, data) => {
|
|
13
|
+
if (err) {
|
|
14
|
+
console.error("Error reading the JSON file:", err);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let config = JSON.parse(data);
|
|
19
|
+
|
|
20
|
+
// Extract the old project name from the projectRootPath
|
|
21
|
+
const oldProjectNameMatch = /[^\\]*$/.exec(config.projectRootPath);
|
|
22
|
+
if (!oldProjectNameMatch) {
|
|
23
|
+
console.error(
|
|
24
|
+
"Could not extract the old project name from projectRootPath."
|
|
25
|
+
);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const oldProjectName = oldProjectNameMatch[0];
|
|
29
|
+
|
|
30
|
+
// Update the projectName
|
|
31
|
+
config.projectName = newProjectName;
|
|
32
|
+
|
|
33
|
+
// Update paths containing the old project name
|
|
34
|
+
config.projectRootPath = config.projectRootPath.replace(
|
|
35
|
+
new RegExp(oldProjectName + "$"),
|
|
36
|
+
newProjectName
|
|
37
|
+
);
|
|
38
|
+
config.bsTarget = config.bsTarget.replace(oldProjectName, newProjectName);
|
|
39
|
+
config.bsPathRewrite["^/"] = config.bsPathRewrite["^/"].replace(
|
|
40
|
+
oldProjectName,
|
|
41
|
+
newProjectName
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Save the updated config back to the JSON file
|
|
45
|
+
fs.writeFile(filePath, JSON.stringify(config, null, 2), "utf8", (err) => {
|
|
46
|
+
if (err) {
|
|
47
|
+
console.error("Error writing the updated JSON file:", err);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
console.log("The project name and paths have been updated successfully.");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Run the function with your config file path and the new project name
|
|
56
|
+
updateProjectNameInConfig(configFilePath, newProjectName);
|
package/dist/src/app/js/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Debounces a function to limit the rate at which it is called.
|
|
3
3
|
*
|
|
4
|
-
* @param {
|
|
5
|
-
* @param {
|
|
6
|
-
* @param {
|
|
7
|
-
* @returns {
|
|
4
|
+
* @param {Function} func - The function to debounce.
|
|
5
|
+
* @param {number} wait - The number of milliseconds to wait before invoking the function.
|
|
6
|
+
* @param {boolean} immediate - Whether to invoke the function immediately on the leading edge.
|
|
7
|
+
* @returns {Function} - The debounced function.
|
|
8
8
|
*/
|
|
9
9
|
function debounce(func, wait, immediate) {
|
|
10
10
|
let timeout;
|
|
@@ -21,50 +21,171 @@ function debounce(func, wait, immediate) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* @async
|
|
27
|
-
* @param {{ className: any; methodName: any; params?: {}; }} param0
|
|
28
|
-
* @param {*} param0.className
|
|
29
|
-
* @param {*} param0.methodName
|
|
30
|
-
* @param {{}} [param0.params={}]
|
|
31
|
-
* @returns {unknown}
|
|
24
|
+
* Represents a HTTP request.
|
|
32
25
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
let requestBody = {
|
|
36
|
-
className: className,
|
|
37
|
-
methodName: methodName,
|
|
38
|
-
params: JSON.stringify(params),
|
|
39
|
-
};
|
|
26
|
+
class RequestApi {
|
|
27
|
+
static instance = null;
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
29
|
+
/**
|
|
30
|
+
* The constructor is now private. To ensure it's not accessible from outside,
|
|
31
|
+
* you can throw an error if someone tries to instantiate it directly
|
|
32
|
+
* (though JavaScript does not have true private constructors).
|
|
33
|
+
*/
|
|
34
|
+
constructor(baseURL = window.location.origin) {
|
|
35
|
+
this.baseURL = baseURL;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Static method to get instance of RequestApi.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} [baseURL=window.location.origin] - The base URL for the request.
|
|
42
|
+
* @returns {RequestApi} The singleton instance of the RequestApi.
|
|
43
|
+
*/
|
|
44
|
+
static getInstance(baseURL = window.location.origin) {
|
|
45
|
+
if (!RequestApi.instance) {
|
|
46
|
+
RequestApi.instance = new RequestApi(baseURL);
|
|
47
|
+
}
|
|
48
|
+
return RequestApi.instance;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Sends a HTTP request.
|
|
53
|
+
*
|
|
54
|
+
* @async
|
|
55
|
+
* @param {string} method - The HTTP method.
|
|
56
|
+
* @param {string} url - The URL to send the request to.
|
|
57
|
+
* @param {*} [data=null] - The data to send with the request.
|
|
58
|
+
* @param {Object} [headers={}] - The headers to include in the request.
|
|
59
|
+
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
60
|
+
*/
|
|
61
|
+
async request(method, url, data = null, headers = {}) {
|
|
62
|
+
let fullUrl = `${this.baseURL}${url}`;
|
|
63
|
+
const options = {
|
|
64
|
+
method,
|
|
65
|
+
headers: {
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
68
|
+
...headers,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (data) {
|
|
73
|
+
if (method === "GET") {
|
|
74
|
+
const params = new URLSearchParams(data).toString();
|
|
75
|
+
fullUrl += `?${params}`;
|
|
76
|
+
} else {
|
|
77
|
+
options.body = JSON.stringify(data);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (data && method !== "GET" && method !== "HEAD" && method !== "OPTIONS") {
|
|
82
|
+
options.body = JSON.stringify(data);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const response = await fetch(fullUrl, options);
|
|
87
|
+
if (method === "HEAD") {
|
|
88
|
+
return response.headers;
|
|
89
|
+
}
|
|
90
|
+
const contentType = response.headers.get("content-type");
|
|
91
|
+
if (contentType && contentType.includes("application/json")) {
|
|
92
|
+
return await response.json();
|
|
93
|
+
} else {
|
|
94
|
+
return await response.text();
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Sends a GET request.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} url - The URL to send the request to.
|
|
105
|
+
* @param {*} [params] - The parameters to include in the request.
|
|
106
|
+
* @param {Object} [headers={}] - The headers to include in the request.
|
|
107
|
+
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
108
|
+
*/
|
|
109
|
+
get(url, params, headers) {
|
|
110
|
+
return this.request("GET", url, params, headers);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Sends a POST request.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} url - The URL to send the request to.
|
|
117
|
+
* @param {*} data - The data to send with the request.
|
|
118
|
+
* @param {Object} [headers={}] - The headers to include in the request.
|
|
119
|
+
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
120
|
+
*/
|
|
121
|
+
post(url, data, headers) {
|
|
122
|
+
return this.request("POST", url, data, headers);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Sends a PUT request.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} url - The URL to send the request to.
|
|
129
|
+
* @param {*} data - The data to send with the request.
|
|
130
|
+
* @param {Object} [headers={}] - The headers to include in the request.
|
|
131
|
+
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
132
|
+
*/
|
|
133
|
+
put(url, data, headers) {
|
|
134
|
+
return this.request("PUT", url, data, headers);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Sends a DELETE request.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} url - The URL to send the request to.
|
|
141
|
+
* @param {*} data - The data to send with the request.
|
|
142
|
+
* @param {Object} [headers={}] - The headers to include in the request.
|
|
143
|
+
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
144
|
+
*/
|
|
145
|
+
delete(url, data, headers) {
|
|
146
|
+
return this.request("DELETE", url, data, headers);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Sends a PATCH request.
|
|
151
|
+
*
|
|
152
|
+
* @param {string} url - The URL to send the request to.
|
|
153
|
+
* @param {*} data - The data to send with the request.
|
|
154
|
+
* @param {Object} [headers={}] - The headers to include in the request.
|
|
155
|
+
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
156
|
+
*/
|
|
157
|
+
patch(url, data, headers) {
|
|
158
|
+
return this.request("PATCH", url, data, headers);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Sends a HEAD request.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} url - The URL to send the request to.
|
|
165
|
+
* @param {Object} [headers={}] - The headers to include in the request.
|
|
166
|
+
* @returns {Promise<unknown>} - A promise that resolves to the response headers.
|
|
167
|
+
*/
|
|
168
|
+
head(url, headers) {
|
|
169
|
+
return this.request("HEAD", url, null, headers);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Sends an OPTIONS request.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} url - The URL to send the request to.
|
|
176
|
+
* @param {Object} [headers={}] - The headers to include in the request.
|
|
177
|
+
* @returns {Promise<unknown>} - A promise that resolves to the options available.
|
|
178
|
+
*/
|
|
179
|
+
options(url, headers) {
|
|
180
|
+
return this.request("OPTIONS", url, null, headers);
|
|
181
|
+
}
|
|
61
182
|
}
|
|
62
183
|
|
|
63
184
|
/**
|
|
64
|
-
*
|
|
185
|
+
* Copies text to the clipboard.
|
|
65
186
|
*
|
|
66
|
-
* @param {
|
|
67
|
-
* @param {
|
|
187
|
+
* @param {string} text - The text to copy.
|
|
188
|
+
* @param {HTMLElement} btnElement - The button element that triggered the copy action.
|
|
68
189
|
*/
|
|
69
190
|
function copyToClipboard(text, btnElement) {
|
|
70
191
|
navigator.clipboard.writeText(text).then(
|
|
@@ -89,9 +210,9 @@ function copyToClipboard(text, btnElement) {
|
|
|
89
210
|
}
|
|
90
211
|
|
|
91
212
|
/**
|
|
92
|
-
*
|
|
213
|
+
* Copies code to the clipboard.
|
|
93
214
|
*
|
|
94
|
-
* @param {
|
|
215
|
+
* @param {HTMLElement} btnElement - The button element that triggered the copy action.
|
|
95
216
|
*/
|
|
96
217
|
function copyCode(btnElement) {
|
|
97
218
|
// Assuming your code block is uniquely identifiable close to your button
|
|
@@ -105,25 +226,27 @@ function copyCode(btnElement) {
|
|
|
105
226
|
}
|
|
106
227
|
|
|
107
228
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* @class StateManager
|
|
111
|
-
* @typedef {StateManager}
|
|
229
|
+
* Manages the application state.
|
|
112
230
|
*/
|
|
113
231
|
class StateManager {
|
|
114
232
|
static instance = null;
|
|
115
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Creates a new StateManager instance.
|
|
236
|
+
*
|
|
237
|
+
* @param {{}} [initialState={}] - The initial state.
|
|
238
|
+
*/
|
|
116
239
|
constructor(initialState = {}) {
|
|
117
240
|
this.state = initialState;
|
|
118
241
|
this.listeners = [];
|
|
119
242
|
}
|
|
120
243
|
|
|
121
244
|
/**
|
|
122
|
-
*
|
|
245
|
+
* Gets the singleton instance of StateManager.
|
|
123
246
|
*
|
|
124
247
|
* @static
|
|
125
|
-
* @param {{}} [initialState={}]
|
|
126
|
-
* @returns {
|
|
248
|
+
* @param {{}} [initialState={}] - The initial state.
|
|
249
|
+
* @returns {StateManager} - The StateManager instance.
|
|
127
250
|
*/
|
|
128
251
|
static getInstance(initialState = {}) {
|
|
129
252
|
if (!StateManager.instance) {
|
|
@@ -134,10 +257,10 @@ class StateManager {
|
|
|
134
257
|
}
|
|
135
258
|
|
|
136
259
|
/**
|
|
137
|
-
*
|
|
260
|
+
* Sets the state.
|
|
138
261
|
*
|
|
139
|
-
* @param {*} update
|
|
140
|
-
* @param {boolean} [saveToStorage=false]
|
|
262
|
+
* @param {*} update - The state update.
|
|
263
|
+
* @param {boolean} [saveToStorage=false] - Whether to save the state to localStorage.
|
|
141
264
|
*/
|
|
142
265
|
setState(update, saveToStorage = false) {
|
|
143
266
|
this.state = { ...this.state, ...update };
|
|
@@ -148,10 +271,10 @@ class StateManager {
|
|
|
148
271
|
}
|
|
149
272
|
|
|
150
273
|
/**
|
|
151
|
-
*
|
|
274
|
+
* Subscribes to state changes.
|
|
152
275
|
*
|
|
153
|
-
* @param {*} listener
|
|
154
|
-
* @returns {
|
|
276
|
+
* @param {*} listener - The listener function.
|
|
277
|
+
* @returns {Function} - A function to unsubscribe the listener.
|
|
155
278
|
*/
|
|
156
279
|
subscribe(listener) {
|
|
157
280
|
this.listeners.push(listener);
|
|
@@ -160,10 +283,16 @@ class StateManager {
|
|
|
160
283
|
(this.listeners = this.listeners.filter((l) => l !== listener));
|
|
161
284
|
}
|
|
162
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Saves the state to localStorage.
|
|
288
|
+
*/
|
|
163
289
|
saveState() {
|
|
164
290
|
localStorage.setItem("appState", JSON.stringify(this.state));
|
|
165
291
|
}
|
|
166
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Loads the state from localStorage.
|
|
295
|
+
*/
|
|
167
296
|
loadState() {
|
|
168
297
|
const state = localStorage.getItem("appState");
|
|
169
298
|
if (state) {
|
|
@@ -173,9 +302,9 @@ class StateManager {
|
|
|
173
302
|
}
|
|
174
303
|
|
|
175
304
|
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* @param {boolean} [clearFromStorage=false]
|
|
305
|
+
* Resets the state to its initial value.
|
|
306
|
+
*
|
|
307
|
+
* @param {boolean} [clearFromStorage=false] - Whether to clear the state from localStorage.
|
|
179
308
|
*/
|
|
180
309
|
resetState(clearFromStorage = false) {
|
|
181
310
|
this.state = {}; // Reset the state to an empty object or a default state if you prefer
|
|
@@ -186,5 +315,5 @@ class StateManager {
|
|
|
186
315
|
}
|
|
187
316
|
}
|
|
188
317
|
|
|
189
|
-
|
|
190
|
-
const
|
|
318
|
+
const store = StateManager.getInstance();
|
|
319
|
+
const api = RequestApi.getInstance();
|
package/package.json
CHANGED
package/dist/src/app/api/api.php
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
// CORS headers
|
|
4
|
-
header('Access-Control-Allow-Origin: *');
|
|
5
|
-
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
|
6
|
-
header('Access-Control-Allow-Headers: Content-Type, X-Requested-With, Accept, Origin, Authorization, X-CSRF-Token');
|
|
7
|
-
header('Content-Type: application/json');
|
|
8
|
-
|
|
9
|
-
// Preflight request handling
|
|
10
|
-
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
|
11
|
-
exit(0);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Check for valid POST request with XMLHttpRequest
|
|
15
|
-
if ($_SERVER["REQUEST_METHOD"] !== "POST" || empty($_SERVER["HTTP_X_REQUESTED_WITH"]) || $_SERVER["HTTP_X_REQUESTED_WITH"] != "XMLHttpRequest") {
|
|
16
|
-
http_response_code(400); // Bad Request
|
|
17
|
-
echo json_encode(["error" => "Invalid request method or type."]);
|
|
18
|
-
exit;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
require_once __DIR__ . "/../../../settings/paths.php";
|
|
22
|
-
require_once __DIR__ . "/../../../vendor/autoload.php";
|
|
23
|
-
|
|
24
|
-
use Lib\Prisma\Classes\Prisma;
|
|
25
|
-
|
|
26
|
-
// Initialize variables
|
|
27
|
-
$className = $methodName = $paramsJson = "";
|
|
28
|
-
$params = null;
|
|
29
|
-
|
|
30
|
-
// Determine request content type
|
|
31
|
-
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
|
32
|
-
|
|
33
|
-
// Handle JSON content type
|
|
34
|
-
if (stripos($contentType, 'application/json') !== false) {
|
|
35
|
-
$inputJSON = file_get_contents('php://input');
|
|
36
|
-
$input = json_decode($inputJSON, true);
|
|
37
|
-
if (json_last_error() === JSON_ERROR_NONE) {
|
|
38
|
-
$className = $input['className'] ?? '';
|
|
39
|
-
$methodName = $input['methodName'] ?? '';
|
|
40
|
-
// Directly assign $params without double decoding
|
|
41
|
-
$params = $input['params'] ?? []; // Initialize as empty array if not set
|
|
42
|
-
} else {
|
|
43
|
-
echo json_encode(['error' => 'Error: Invalid JSON body!']);
|
|
44
|
-
exit;
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
// Handle form-urlencoded data
|
|
48
|
-
$className = $_POST["className"] ?? "";
|
|
49
|
-
$methodName = $_POST["methodName"] ?? "";
|
|
50
|
-
$paramsJson = $_POST["params"] ?? "";
|
|
51
|
-
if (!empty($paramsJson)) {
|
|
52
|
-
$params = json_decode($paramsJson, true);
|
|
53
|
-
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
54
|
-
echo json_encode(['error' => 'Error: Invalid JSON in params!']);
|
|
55
|
-
exit;
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
$params = []; // Initialize as empty array if not set
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Construct the full class name and check for class and property existence
|
|
63
|
-
$fullClassName = "Lib\\Prisma\\Classes\\" . pascalCase($className);
|
|
64
|
-
if (!class_exists($fullClassName) || !property_exists(Prisma::class, camelCase($className))) {
|
|
65
|
-
echo json_encode(['error' => "Error: Class $fullClassName not found or property $className not found in Prisma class!"]);
|
|
66
|
-
exit;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Create an instance of the class
|
|
70
|
-
$instance = (new Prisma())->$className;
|
|
71
|
-
|
|
72
|
-
// Check for method existence
|
|
73
|
-
if (!method_exists($instance, $methodName)) {
|
|
74
|
-
echo json_encode(['error' => "Error: Method $methodName not found in class $fullClassName!"]);
|
|
75
|
-
exit;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
$result = $instance->$methodName($params);
|
|
80
|
-
echo json_encode(['result' => $result instanceof \stdClass ? (array)$result : $result]);
|
|
81
|
-
} catch (\ArgumentCountError | \Exception $e) {
|
|
82
|
-
echo json_encode(['error' => "Error: " . $e->getMessage()]);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function camelCase($string)
|
|
86
|
-
{
|
|
87
|
-
$string = ucwords($string, "_");
|
|
88
|
-
$string = str_replace("_", "", $string);
|
|
89
|
-
$string = lcfirst($string);
|
|
90
|
-
return $string;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function pascalCase($string)
|
|
94
|
-
{
|
|
95
|
-
$string = ucwords($string, "_");
|
|
96
|
-
$string = str_replace("_", "", $string);
|
|
97
|
-
return $string;
|
|
98
|
-
}
|