@artilingo/artiframe-cli 2.0.4 → 3.0.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.
Files changed (91) hide show
  1. package/LICENSE +660 -660
  2. package/README.md +64 -118
  3. package/bin/artiframe.js +0 -0
  4. package/bin/artiframe.php +0 -0
  5. package/core-stubs/app/ApiAuthControl.php +76 -0
  6. package/core-stubs/app/ApiControl.php +81 -35
  7. package/core-stubs/app/AuthControl.php +39 -0
  8. package/core-stubs/app/Database.php +36 -35
  9. package/core-stubs/app/DotEnv.php +19 -1
  10. package/core-stubs/app/ViewControl.php +47 -1
  11. package/core-stubs/bin/SQLMethod.php +261 -0
  12. package/core-stubs/bin/SystemMethod.php +115 -74
  13. package/core-stubs/bin/ViewMethod.php +19 -5
  14. package/core-stubs/docs/de.html +4325 -4347
  15. package/core-stubs/docs/en.html +4325 -4347
  16. package/core-stubs/docs/es.html +4325 -4347
  17. package/core-stubs/docs/fr.html +4325 -4347
  18. package/core-stubs/docs/tr.html +117 -46
  19. package/core-stubs/public/403.php +52 -0
  20. package/core-stubs/public/assets/css/components/header.css +174 -0
  21. package/core-stubs/public/assets/css/components/mobilenav.css +198 -0
  22. package/core-stubs/public/assets/css/components/sidebar.css +184 -0
  23. package/core-stubs/public/assets/css/root/app.css +975 -38
  24. package/core-stubs/public/assets/css/root/root.css +64 -0
  25. package/core-stubs/public/assets/js/components/header.js +380 -0
  26. package/core-stubs/public/assets/js/components/mobilenav.js +97 -0
  27. package/core-stubs/public/assets/js/components/theme-modal.js +74 -0
  28. package/core-stubs/public/assets/js/root/app.js +1870 -0
  29. package/core-stubs/public/includes/footer.php +4 -5
  30. package/core-stubs/public/includes/head-auth.php +65 -0
  31. package/core-stubs/public/includes/head.php +56 -17
  32. package/core-stubs/public/includes/header.php +135 -1
  33. package/core-stubs/public/includes/mobilenav.php +75 -3
  34. package/core-stubs/public/includes/sidebar.php +186 -1
  35. package/core-stubs/public/includes/theme-modal.php +1 -0
  36. package/db-studio/debug.log +3 -0
  37. package/db-studio/index.html +1755 -0
  38. package/db-studio/launcher.html +102 -0
  39. package/db-studio/launcher.js +180 -0
  40. package/db-studio/logo.svg +22 -0
  41. package/db-studio/main.js +568 -0
  42. package/db-studio/package-lock.json +1034 -0
  43. package/db-studio/package.json +16 -0
  44. package/db-studio/preload.js +29 -0
  45. package/devops-studio/api-inspector.js +222 -0
  46. package/devops-studio/index.html +1892 -0
  47. package/devops-studio/logo.svg +22 -0
  48. package/devops-studio/main.js +67 -0
  49. package/devops-studio/make-wizard.js +635 -0
  50. package/devops-studio/package-lock.json +871 -0
  51. package/devops-studio/package.json +12 -0
  52. package/package.json +5 -2
  53. package/scripts/postinstall.js +44 -8
  54. package/server-studio/.json-404-guard +1 -0
  55. package/server-studio/index.html +283 -0
  56. package/server-studio/logo.svg +22 -0
  57. package/server-studio/main.js +133 -0
  58. package/server-studio/package-lock.json +1179 -0
  59. package/server-studio/package.json +15 -0
  60. package/server-studio/router.php +99 -0
  61. package/src/App.php +29 -0
  62. package/src/Commands/AddCommand.php +26 -9
  63. package/src/Commands/DbExportCommand.php +131 -0
  64. package/src/Commands/DbStartCommand.php +46 -0
  65. package/src/Commands/DevopsCommand.php +35 -0
  66. package/src/Commands/GoCommand.php +48 -48
  67. package/src/Commands/ListCommand.php +68 -68
  68. package/src/Commands/MakeApiCommand.php +6 -1
  69. package/src/Commands/MakeViewCommand.php +25 -2
  70. package/src/Commands/NewProjectCommand.php +16 -11
  71. package/src/Commands/OptimizeCommand.php +61 -0
  72. package/src/Commands/RemoveCommand.php +35 -7
  73. package/src/Commands/ServeCommand.php +46 -91
  74. package/src/Commands/ShowCommand.php +24 -24
  75. package/src/Commands/UpgradeCommand.php +39 -10
  76. package/stubs/make/api-switch-case.stub +1 -1
  77. package/stubs/make/auth-api-standart.stub +20 -0
  78. package/stubs/make/auth-api-switch-case.stub +38 -0
  79. package/stubs/make/auth-view.stub +26 -0
  80. package/stubs/make/class.stub +10 -2
  81. package/stubs/make/view.stub +3 -0
  82. package/stubs/service/image.stub +334 -334
  83. package/stubs/service/iyzico.stub +637 -637
  84. package/stubs/service/jwt.stub +312 -312
  85. package/stubs/service/phpmailer.stub +143 -143
  86. package/stubs/service/pusher.stub +232 -232
  87. package/stubs/service/qrcode.stub +169 -169
  88. package/stubs/service/redis.stub +361 -361
  89. package/stubs/service/s3.stub +377 -377
  90. package/stubs/service/stripe.stub +526 -526
  91. package/stubs/service/twilio.stub +361 -361
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "artiframe-devops-studio",
3
+ "version": "1.0.0",
4
+ "description": "ArtiFrame DevOps Studio",
5
+ "main": "main.js",
6
+ "scripts": {
7
+ "start": "electron ."
8
+ },
9
+ "devDependencies": {
10
+ "electron": "^28.2.0"
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artilingo/artiframe-cli",
3
- "version": "2.0.4",
3
+ "version": "3.0.0",
4
4
  "description": "ArtiFrame — Zero-dependency native PHP framework with a powerful multilingual CLI. Scaffold projects, generate views, APIs and classes instantly.",
5
5
  "main": "bin/artiframe.js",
6
6
  "bin": {
@@ -18,7 +18,10 @@
18
18
  "stubs/",
19
19
  "assets/",
20
20
  "scripts/",
21
- "LICENSE"
21
+ "LICENSE",
22
+ "devops-studio/",
23
+ "db-studio/",
24
+ "server-studio/"
22
25
  ],
23
26
  "repository": {
24
27
  "type": "git",
@@ -2,20 +2,56 @@
2
2
  * ArtiFrame CLI — postinstall script
3
3
  *
4
4
  * Sets the execute permission on bin/artiframe.php on Unix-like systems.
5
- * Silently skipped on Windows (not needed).
5
+ * Creates the default global Workspace directory (C:\ArtiFrame or ~/ArtiFrame).
6
6
  */
7
7
 
8
8
  const fs = require('fs');
9
9
  const path = require('path');
10
+ const os = require('os');
10
11
 
11
- if (process.platform === 'win32') {
12
- process.exit(0);
12
+ // 1. CHMOD for Unix
13
+ const target = path.join(__dirname, '..', 'bin', 'artiframe.php');
14
+ if (process.platform !== 'win32') {
15
+ try {
16
+ fs.chmodSync(target, 0o755);
17
+ } catch (e) {}
13
18
  }
14
19
 
15
- const target = path.join(__dirname, '..', 'bin', 'artiframe.php');
20
+ // 2. Create Default Workspace
21
+ const defaultWorkspace = process.platform === 'win32'
22
+ ? 'C:\\ArtiFrame'
23
+ : path.join(os.homedir(), 'ArtiFrame');
16
24
 
17
- try {
18
- fs.chmodSync(target, 0o755);
19
- } catch (e) {
20
- // Non-fatal — if permissions fail (e.g. read-only fs), skip silently.
25
+ if (!fs.existsSync(defaultWorkspace)) {
26
+ try {
27
+ fs.mkdirSync(defaultWorkspace, { recursive: true });
28
+
29
+ // Put a nice greeting file
30
+ const greeting = `ArtiFrame Workspace\n===================\nWelcome to the ArtiFrame ecosystem!\n\nAll your DevOps Studio projects will be created and managed here.\nTo start the studio, simply run:\n\n> artiframe devops\n`;
31
+ fs.writeFileSync(path.join(defaultWorkspace, 'README.txt'), greeting);
32
+
33
+ console.log('\n\x1b[32m%s\x1b[0m', '✅ ArtiFrame workspace created at: ' + defaultWorkspace);
34
+ console.log('\x1b[36m%s\x1b[0m', ' Run `artiframe devops` to launch the studio!\n');
35
+ } catch (e) {
36
+ // Silently fail if no permissions
37
+ }
38
+ } else {
39
+ console.log('\n\x1b[36m%s\x1b[0m', '✅ ArtiFrame workspace ready at: ' + defaultWorkspace);
40
+ console.log('\x1b[36m%s\x1b[0m', ' Run `artiframe devops` to launch the studio!\n');
21
41
  }
42
+
43
+
44
+ // 3. Install dependencies for studios
45
+ const { execSync } = require('child_process');
46
+
47
+ ['devops-studio', 'db-studio', 'server-studio'].forEach(studio => {
48
+ const studioPath = path.join(__dirname, '..', studio);
49
+ if (fs.existsSync(studioPath)) {
50
+ console.log('\x1b[36m%s\x1b[0m', `📦 Installing dependencies for ${studio}...`);
51
+ try {
52
+ execSync('npm install --production', { cwd: studioPath, stdio: 'ignore' });
53
+ } catch (e) {
54
+ console.log('\x1b[31m%s\x1b[0m', `❌ Failed to install dependencies for ${studio}`);
55
+ }
56
+ }
57
+ });
@@ -0,0 +1 @@
1
+ 1
@@ -0,0 +1,283 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ArtiFrame Server Studio</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@400;500;600;700&display=swap');
9
+
10
+ body { margin: 0; padding: 20px; background-color: #0d1117; color: #c9d1d9; font-family: 'Inter', sans-serif; display: flex; flex-direction: column; height: 100vh; box-sizing: border-box; }
11
+
12
+ .header { display: flex; justify-content: space-between; align-items: center; background: #161b22; padding: 15px 25px; border-radius: 12px; border: 1px solid #30363d; margin-bottom: 20px; }
13
+ .logo { font-size: 24px; font-weight: 700; color: #f0f6fc; }
14
+ .logo span { color: #2ea043; }
15
+
16
+ .status-pill { background: #238636; color: #fff; padding: 8px 16px; border-radius: 20px; font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
17
+ .status-pill.stopped { background: #da3633; }
18
+
19
+ .controls { display: flex; align-items: center; gap: 15px; }
20
+ .btn-stop { background: #da3633; color: white; border: none; padding: 10px 20px; border-radius: 6px; font-weight: 600; cursor: pointer; transition: 0.2s; }
21
+ .btn-stop:hover { background: #b62324; }
22
+ .btn-start { background: #238636; color: white; border: none; padding: 10px 20px; border-radius: 6px; font-weight: 600; cursor: pointer; transition: 0.2s; display: none; }
23
+
24
+ .port-input { background: #0d1117; border: 1px solid #30363d; color: white; padding: 10px; border-radius: 6px; width: 100px; font-family: 'JetBrains Mono', monospace; outline: none; }
25
+
26
+ .main-content { display: flex; gap: 20px; flex: 1; min-height: 0; }
27
+
28
+ .left-pane { width: 380px; flex-shrink: 0; display: flex; flex-direction: column; gap: 20px; }
29
+ .panel { background: #161b22; border: 1px solid #30363d; border-radius: 12px; padding: 20px; }
30
+
31
+ .address-box { margin-bottom: 15px; }
32
+ .address-label { font-size: 13px; color: #8b949e; margin-bottom: 8px; }
33
+ .address-value { background: #0d1117; border: 1px solid #30363d; padding: 12px; border-radius: 6px; font-family: 'JetBrains Mono', monospace; font-size: 14px; margin-bottom: 10px; }
34
+ .btn-browser { width: 100%; background: #21262d; border: 1px solid #30363d; color: #c9d1d9; padding: 10px; border-radius: 6px; cursor: pointer; font-weight: 600; transition: 0.2s; }
35
+ .btn-browser:hover { background: #30363d; }
36
+
37
+ .qr-container { display: flex; flex-direction: column; align-items: center; justify-content: center; background: #0d1117; border: 1px solid #30363d; padding: 20px; border-radius: 12px; margin-top: 10px; position: relative; }
38
+ .qr-text { font-size: 12px; color: #8b949e; text-align: center; margin-top: 20px; }
39
+ .qr-logo { position: absolute; width: 40px; height: 40px; background: #0d1117; color: white; display: flex; align-items: center; justify-content: center; border-radius: 8px; box-shadow: 0 0 0 5px #0d1117; top: calc(50% - 30px); }
40
+
41
+ .right-pane { flex: 1; display: flex; flex-direction: column; background: #161b22; border: 1px solid #30363d; border-radius: 12px; overflow: hidden; }
42
+ .right-header { padding: 20px; border-bottom: 1px solid #30363d; }
43
+ .right-title { font-weight: 600; font-size: 16px; margin: 0 0 5px 0; color: #f0f6fc; }
44
+ .right-subtitle { font-size: 13px; color: #8b949e; margin: 0; }
45
+
46
+ .log-container { flex: 1; overflow-y: auto; padding: 10px; font-family: 'JetBrains Mono', monospace; font-size: 13px; }
47
+ .log-table { width: 100%; border-collapse: collapse; }
48
+ .log-table th { text-align: left; padding: 10px 15px; color: #8b949e; font-weight: 500; border-bottom: 1px solid #30363d; position: sticky; top: 0; background: #161b22; }
49
+ .log-table td { padding: 12px 15px; border-bottom: 1px solid #21262d; }
50
+
51
+ .badge { padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 700; color: white; display: inline-block; text-align: center; min-width: 50px; }
52
+ .badge-get { background: #2ea043; }
53
+ .badge-post { background: #3182ce; }
54
+ .badge-put { background: #d97706; }
55
+ .badge-del { background: #da3633; }
56
+ .badge-other { background: #6e7681; }
57
+
58
+ .status-200 { background: #2ea043; }
59
+ .status-404 { background: #da3633; }
60
+ .status-500 { background: #a371f7; }
61
+
62
+ .fallback-guard { padding: 15px 20px; border-top: 1px solid #30363d; display: flex; align-items: center; gap: 10px; font-size: 14px; }
63
+
64
+ /* Toggle Switch */
65
+ .switch { position: relative; display: inline-block; width: 40px; height: 20px; }
66
+ .switch input { opacity: 0; width: 0; height: 0; }
67
+ .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #30363d; transition: .4s; border-radius: 34px; }
68
+ .slider:before { position: absolute; content: ""; height: 14px; width: 14px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }
69
+ input:checked + .slider { background-color: #2ea043; }
70
+ input:checked + .slider:before { transform: translateX(20px); }
71
+
72
+ </style>
73
+ </head>
74
+ <body>
75
+ <div class="header">
76
+ <div class="logo" style="display: flex; align-items: center; gap: 10px;"><img src="logo.svg" style="height: 32px; width: auto;" alt="Logo"><div><span>Arti</span>Frame Studio</div></div>
77
+
78
+ <div class="status-pill" id="status-indicator">
79
+ <span id="status-icon">🟢</span> <span id="status-text">SERVER RUNNING &mdash; 0.0.0.0:9002</span>
80
+ </div>
81
+
82
+ <div class="controls">
83
+ <button class="btn-stop" id="btn-stop">Stop Server (CTRL+C)</button>
84
+ <button class="btn-start" id="btn-start">Start Server</button>
85
+ <div style="display: flex; align-items: center; gap: 10px; color: #8b949e; font-size: 14px;">
86
+ Change this port:
87
+ <input type="number" class="port-input" id="port-input" value="9002">
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ <div class="main-content">
93
+ <!-- Left Pane -->
94
+ <div class="left-pane">
95
+ <div class="panel">
96
+ <div class="address-box">
97
+ <div class="address-label">Local address:</div>
98
+ <div class="address-value" id="val-local">http://localhost:9002</div>
99
+ <button class="btn-browser" onclick="openBrowser('local')">Open Browser</button>
100
+ </div>
101
+
102
+ <div class="address-box" style="margin-bottom:0;">
103
+ <div class="address-label">Network address (Mobile debugging):</div>
104
+ <div class="address-value" id="val-network">http://192.168.1.x:9002</div>
105
+ </div>
106
+ </div>
107
+
108
+ <div class="panel" style="flex:1; display:flex; flex-direction:column; justify-content:center;">
109
+ <div class="qr-container">
110
+ <canvas id="qr-canvas"></canvas>
111
+ <div class="qr-logo"><img src="logo.svg" style="width: 28px; height: 28px;" alt="Logo"></div>
112
+ </div>
113
+ <div class="qr-text">Scan with your phone camera to test responsive views instantly on local network</div>
114
+ </div>
115
+ </div>
116
+
117
+ <!-- Right Pane -->
118
+ <div class="right-pane">
119
+ <div class="right-header" style="display: flex; justify-content: space-between; align-items: center;">
120
+ <div>
121
+ <h3 class="right-title">Real-time HTTP Request & Route Gateway Logger</h3>
122
+ <p class="right-subtitle">Live console streaming incoming HTTP requests via JetBrains Mono console typography.</p>
123
+ </div>
124
+ <button class="btn-browser" style="width: auto; background: #21262d; border-color: #30363d;" onclick="document.getElementById('log-body').innerHTML=''">🗑️ Temizle</button>
125
+ </div>
126
+
127
+ <div class="log-container" id="log-scroll">
128
+ <table class="log-table">
129
+ <thead>
130
+ <tr>
131
+ <th>Time</th>
132
+ <th>Method</th>
133
+ <th>Route URL</th>
134
+ <th>Resolved File</th>
135
+ <th>Parsed Params</th>
136
+ <th>Süre & Bellek</th>
137
+ <th>Status Code</th>
138
+ </tr>
139
+ </thead>
140
+ <tbody id="log-body">
141
+ <!-- Logs injected here -->
142
+ </tbody>
143
+ </table>
144
+ </div>
145
+
146
+ <div class="fallback-guard">
147
+ <label class="switch">
148
+ <input type="checkbox" id="guard-toggle">
149
+ <span class="slider"></span>
150
+ </label>
151
+ <span>API Fallback (JSON 404 Guard)</span>
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ <script>
157
+ const { ipcRenderer } = require('electron');
158
+ const QRCode = require('qrcode');
159
+ const fs = require('fs');
160
+ const path = require('path');
161
+
162
+ let currentPort = 9002;
163
+ let networkIp = '127.0.0.1';
164
+ let isRunning = false;
165
+
166
+ const body = document.getElementById('log-body');
167
+ const scroll = document.getElementById('log-scroll');
168
+ const guardToggle = document.getElementById('guard-toggle');
169
+
170
+ function appendLog(log) {
171
+ const tr = document.createElement('tr');
172
+
173
+ let mBadge = 'badge-other';
174
+ if(log.method === 'GET') mBadge = 'badge-get';
175
+ if(log.method === 'POST') mBadge = 'badge-post';
176
+ if(log.method === 'PUT') mBadge = 'badge-put';
177
+ if(log.method === 'DELETE') mBadge = 'badge-del';
178
+
179
+ let sBadge = 'status-200';
180
+ let sText = '200 OK';
181
+ if(log.statusCode === 404) { sBadge = 'status-404'; sText = '404 Not Found'; }
182
+ else if(log.statusCode >= 500) { sBadge = 'status-500'; sText = log.statusCode + ' Error'; }
183
+ else { sText = log.statusCode + ' OK'; }
184
+
185
+ tr.innerHTML = `
186
+ <td style="color:#8b949e;">${log.time}</td>
187
+ <td><span class="badge ${mBadge}">${log.method}</span></td>
188
+ <td style="color:#f0f6fc;">${log.url}</td>
189
+ <td style="color:#d2a8ff;">${log.resolvedFile}</td>
190
+ <td style="color:#a5d6ff;">${log.params || '-'}</td>
191
+ <td style="color:#8b949e; font-size:12px;">${log.duration || '-'} <br> ${log.memory || '-'}</td>
192
+ <td><span class="badge ${sBadge}">${sText}</span></td>
193
+ `;
194
+
195
+ body.appendChild(tr);
196
+
197
+ if (log.errorMessage) {
198
+ const errTr = document.createElement('tr');
199
+ errTr.innerHTML = `<td colspan="6" style="padding: 10px 15px; background: rgba(218, 54, 51, 0.1); color: #ff7b72; font-size: 13px; border-bottom: 1px solid #21262d; white-space: pre-wrap; word-break: break-all;">⚠️ <b>Error:</b> ${log.errorMessage}</td>`;
200
+ body.appendChild(errTr);
201
+ }
202
+
203
+ if(body.childElementCount > 100) {
204
+ // Remove older rows to keep memory in check
205
+ while(body.childElementCount > 100) {
206
+ body.removeChild(body.firstChild);
207
+ }
208
+ }
209
+
210
+ // Auto scroll to bottom
211
+ scroll.scrollTop = scroll.scrollHeight;
212
+ }
213
+
214
+ function updateUI() {
215
+ document.getElementById('val-local').innerText = `http://localhost:${currentPort}`;
216
+ const netUrl = `http://${networkIp}:${currentPort}`;
217
+ document.getElementById('val-network').innerText = netUrl;
218
+
219
+ if (isRunning) {
220
+ document.getElementById('status-indicator').className = 'status-pill';
221
+ document.getElementById('status-icon').innerText = '🟢';
222
+ document.getElementById('status-text').innerHTML = `SERVER RUNNING &mdash; 0.0.0.0:${currentPort}`;
223
+ document.getElementById('btn-stop').style.display = 'block';
224
+ document.getElementById('btn-start').style.display = 'none';
225
+ } else {
226
+ document.getElementById('status-indicator').className = 'status-pill stopped';
227
+ document.getElementById('status-icon').innerText = '🔴';
228
+ document.getElementById('status-text').innerHTML = 'SERVER STOPPED';
229
+ document.getElementById('btn-stop').style.display = 'none';
230
+ document.getElementById('btn-start').style.display = 'block';
231
+ }
232
+
233
+ // Draw QR
234
+ const canvas = document.getElementById('qr-canvas');
235
+ QRCode.toCanvas(canvas, netUrl, { width: 180, margin: 1, errorCorrectionLevel: 'H', color: { dark: '#ffffff', light: '#0d1117' } });
236
+ }
237
+
238
+ ipcRenderer.on('server-status', (e, data) => {
239
+ isRunning = data.running;
240
+ currentPort = data.port;
241
+ networkIp = data.ip;
242
+ document.getElementById('port-input').value = currentPort;
243
+ updateUI();
244
+ });
245
+
246
+ ipcRenderer.on('new-log', (e, log) => {
247
+ appendLog(log);
248
+ });
249
+
250
+ document.getElementById('btn-stop').onclick = () => {
251
+ ipcRenderer.invoke('stop-server');
252
+ };
253
+
254
+ document.getElementById('btn-start').onclick = () => {
255
+ ipcRenderer.invoke('change-port', document.getElementById('port-input').value);
256
+ };
257
+
258
+ document.getElementById('port-input').addEventListener('change', (e) => {
259
+ ipcRenderer.invoke('change-port', e.target.value);
260
+ });
261
+
262
+ function openBrowser(type) {
263
+ const url = type === 'local' ? `http://localhost:${currentPort}` : `http://${networkIp}:${currentPort}`;
264
+ ipcRenderer.invoke('open-external', url);
265
+ }
266
+
267
+ // JSON 404 Guard Toggle Logic (creates a temp file that router.php reads)
268
+ guardToggle.addEventListener('change', (e) => {
269
+ const tempFile = path.join(__dirname, '.json-404-guard');
270
+ if(e.target.checked) {
271
+ fs.writeFileSync(tempFile, '1');
272
+ } else {
273
+ if(fs.existsSync(tempFile)) fs.unlinkSync(tempFile);
274
+ }
275
+ });
276
+
277
+ // Initial state
278
+ if(fs.existsSync(path.join(__dirname, '.json-404-guard'))) {
279
+ guardToggle.checked = true;
280
+ }
281
+ </script>
282
+ </body>
283
+ </html>
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="katman_2" data-name="katman 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144.49 94.09">
3
+ <defs>
4
+ <style>
5
+ .cls-1 {
6
+ fill: #099772;
7
+ }
8
+
9
+ .cls-2 {
10
+ fill: #0a9872;
11
+ }
12
+ </style>
13
+ </defs>
14
+ <g id="katman_1" data-name="katman 1">
15
+ <g>
16
+ <path class="cls-2" d="M131.15,38.06c.67-.18,1.4-.1,2.26.14.32-3.12,2.59-5.34,5.55-5.33s5.5,2.5,5.53,5.43-2.23,5.38-5.12,5.59c-3.09.22-5.71-2.08-5.9-5.29-34.52,2.36-67.93,12.42-97.08,31.26-5.61,3.7-11,7.44-16,11.83l-6.13,12.38-14.26-.02,13.95-27.62,7.14-14.06L47.82.06l21.37,41.64c-3.96,1.03-7.73,2.29-11.62,3.65l-9.74-19.89-2.9,5.99-14.53,29.82c25.42-14.09,52.79-21.29,81.35-23.22l5.69-.27,11.17.03c.86,0,1.58.49,2.52.24Z"/>
17
+ <polygon class="cls-2" points="64.24 58.84 75.54 54.2 90.45 83.55 112.61 83.52 112.61 44.51 122.99 43.06 122.99 94.08 81.55 94.09 72.23 75.24 64.24 58.84"/>
18
+ <path class="cls-1" d="M112.61,33.97V10.34s-53.64.02-53.64.02L54.05.7c.04-.06.09-.22.35-.22h68.33s0,33.6,0,33.6l-10.13-.11Z"/>
19
+ <path class="cls-2" d="M112.61,33.97l10.13.11V.48s-68.34,0-68.34,0c-.26,0-.31.16-.35.22-.3-.09-.39-.7.07-.7h68.84s0,34.31,0,34.31h-7.91c-.8.25-1.56.34-2.36.21-.07-.2-.1-.34-.1-.56Z"/>
20
+ </g>
21
+ </g>
22
+ </svg>
@@ -0,0 +1,133 @@
1
+ const { app, BrowserWindow, ipcMain, shell } = require('electron');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const { spawn } = require('child_process');
5
+ const http = require('http');
6
+
7
+ let mainWindow;
8
+ let phpProcess;
9
+ let internalLogServer;
10
+ let loggerPort = 0;
11
+
12
+ const args = process.argv.slice(2);
13
+ let projectRoot = args[0] || process.cwd();
14
+ let serverPort = parseInt(args[1]) || 9002;
15
+
16
+ function getLocalIp() {
17
+ const interfaces = os.networkInterfaces();
18
+ for (const name of Object.keys(interfaces)) {
19
+ for (const iface of interfaces[name]) {
20
+ if (iface.family === 'IPv4' && !iface.internal) {
21
+ return iface.address;
22
+ }
23
+ }
24
+ }
25
+ return '127.0.0.1';
26
+ }
27
+
28
+ function startLogServer() {
29
+ return new Promise((resolve) => {
30
+ internalLogServer = http.createServer((req, res) => {
31
+ if (req.method === 'POST') {
32
+ let body = '';
33
+ req.on('data', chunk => { body += chunk.toString(); });
34
+ req.on('end', () => {
35
+ try {
36
+ const logData = JSON.parse(body);
37
+ if (mainWindow) {
38
+ mainWindow.webContents.send('new-log', logData);
39
+ }
40
+ } catch (e) {}
41
+ res.writeHead(200);
42
+ res.end('OK');
43
+ });
44
+ } else {
45
+ res.writeHead(404);
46
+ res.end();
47
+ }
48
+ });
49
+
50
+ internalLogServer.listen(0, '127.0.0.1', () => {
51
+ loggerPort = internalLogServer.address().port;
52
+ resolve();
53
+ });
54
+ });
55
+ }
56
+
57
+ function startPhpServer() {
58
+ if (phpProcess) {
59
+ phpProcess.kill();
60
+ }
61
+
62
+ const routerPath = path.join(__dirname, 'router.php');
63
+
64
+ phpProcess = spawn('php', ['-S', `0.0.0.0:${serverPort}`, '-t', path.join(projectRoot, 'public'), routerPath], {
65
+ cwd: projectRoot,
66
+ env: {
67
+ ...process.env,
68
+ ARTIFRAME_STUDIO_LOGGER_PORT: loggerPort,
69
+ ARTIFRAME_PROJECT_ROOT: projectRoot
70
+ }
71
+ });
72
+
73
+ phpProcess.stdout.on('data', (data) => console.log(`PHP: ${data}`));
74
+ phpProcess.stderr.on('data', (data) => console.log(`PHP ERR: ${data}`));
75
+
76
+ if (mainWindow) {
77
+ mainWindow.webContents.send('server-status', {
78
+ running: true,
79
+ port: serverPort,
80
+ ip: getLocalIp()
81
+ });
82
+ }
83
+ }
84
+
85
+ async function createWindow() {
86
+ await startLogServer();
87
+
88
+ mainWindow = new BrowserWindow({
89
+ width: 1050,
90
+ height: 650,
91
+ minWidth: 900,
92
+ minHeight: 500,
93
+ backgroundColor: '#111418',
94
+ webPreferences: {
95
+ nodeIntegration: true,
96
+ contextIsolation: false
97
+ },
98
+ autoHideMenuBar: true,
99
+ title: 'ArtiFrame Server Studio'
100
+ });
101
+
102
+ mainWindow.loadFile('index.html');
103
+
104
+ mainWindow.webContents.on('did-finish-load', () => {
105
+ startPhpServer();
106
+ });
107
+ }
108
+
109
+ app.whenReady().then(createWindow);
110
+
111
+ app.on('window-all-closed', () => {
112
+ if (phpProcess) phpProcess.kill();
113
+ app.quit();
114
+ });
115
+
116
+ ipcMain.handle('open-external', (e, url) => {
117
+ shell.openExternal(url);
118
+ });
119
+
120
+ ipcMain.handle('change-port', (e, newPort) => {
121
+ serverPort = parseInt(newPort);
122
+ startPhpServer();
123
+ });
124
+
125
+ ipcMain.handle('stop-server', () => {
126
+ if (phpProcess) {
127
+ phpProcess.kill();
128
+ phpProcess = null;
129
+ }
130
+ if (mainWindow) {
131
+ mainWindow.webContents.send('server-status', { running: false, port: serverPort, ip: getLocalIp() });
132
+ }
133
+ });