@artilingo/artiframe-cli 2.0.3 → 2.0.5
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/LICENSE +660 -660
- package/bin/artiframe.js +0 -0
- package/bin/artiframe.php +0 -0
- package/core-stubs/app/ApiAuthControl.php +76 -0
- package/core-stubs/app/ApiControl.php +81 -35
- package/core-stubs/app/AuthControl.php +39 -0
- package/core-stubs/app/Database.php +36 -35
- package/core-stubs/app/DotEnv.php +19 -1
- package/core-stubs/app/ViewControl.php +47 -1
- package/core-stubs/bin/SQLMethod.php +261 -0
- package/core-stubs/bin/SystemMethod.php +115 -74
- package/core-stubs/bin/ViewMethod.php +19 -5
- package/core-stubs/docs/de.html +4325 -4347
- package/core-stubs/docs/en.html +4325 -4347
- package/core-stubs/docs/es.html +4325 -4347
- package/core-stubs/docs/fr.html +4325 -4347
- package/core-stubs/docs/tr.html +117 -46
- package/core-stubs/public/403.php +52 -0
- package/core-stubs/public/assets/css/components/header.css +174 -0
- package/core-stubs/public/assets/css/components/mobilenav.css +198 -0
- package/core-stubs/public/assets/css/components/sidebar.css +184 -0
- package/core-stubs/public/assets/css/root/app.css +975 -38
- package/core-stubs/public/assets/css/root/root.css +64 -0
- package/core-stubs/public/assets/js/components/header.js +380 -0
- package/core-stubs/public/assets/js/components/mobilenav.js +97 -0
- package/core-stubs/public/assets/js/components/theme-modal.js +74 -0
- package/core-stubs/public/assets/js/root/app.js +1870 -0
- package/core-stubs/public/includes/footer.php +4 -5
- package/core-stubs/public/includes/head-auth.php +65 -0
- package/core-stubs/public/includes/head.php +56 -17
- package/core-stubs/public/includes/header.php +135 -1
- package/core-stubs/public/includes/mobilenav.php +75 -3
- package/core-stubs/public/includes/sidebar.php +186 -1
- package/core-stubs/public/includes/theme-modal.php +1 -0
- package/db-studio/debug.log +3 -0
- package/db-studio/index.html +1755 -0
- package/db-studio/launcher.html +102 -0
- package/db-studio/launcher.js +180 -0
- package/db-studio/logo.svg +22 -0
- package/db-studio/main.js +568 -0
- package/db-studio/package-lock.json +1034 -0
- package/db-studio/package.json +16 -0
- package/db-studio/preload.js +29 -0
- package/devops-studio/api-inspector.js +222 -0
- package/devops-studio/index.html +1892 -0
- package/devops-studio/logo.svg +22 -0
- package/devops-studio/main.js +67 -0
- package/devops-studio/make-wizard.js +635 -0
- package/devops-studio/package-lock.json +871 -0
- package/devops-studio/package.json +12 -0
- package/package.json +5 -2
- package/scripts/postinstall.js +44 -8
- package/server-studio/.json-404-guard +1 -0
- package/server-studio/index.html +283 -0
- package/server-studio/logo.svg +22 -0
- package/server-studio/main.js +133 -0
- package/server-studio/package-lock.json +1179 -0
- package/server-studio/package.json +15 -0
- package/server-studio/router.php +99 -0
- package/src/App.php +29 -0
- package/src/Commands/AddCommand.php +26 -9
- package/src/Commands/DbExportCommand.php +131 -0
- package/src/Commands/DbStartCommand.php +46 -0
- package/src/Commands/DevopsCommand.php +35 -0
- package/src/Commands/GoCommand.php +48 -48
- package/src/Commands/ListCommand.php +68 -68
- package/src/Commands/MakeApiCommand.php +6 -1
- package/src/Commands/MakeViewCommand.php +25 -2
- package/src/Commands/NewProjectCommand.php +16 -11
- package/src/Commands/OptimizeCommand.php +61 -0
- package/src/Commands/RemoveCommand.php +36 -7
- package/src/Commands/ServeCommand.php +46 -91
- package/src/Commands/ShowCommand.php +24 -24
- package/src/Commands/UpgradeCommand.php +39 -10
- package/stubs/make/api-switch-case.stub +1 -1
- package/stubs/make/auth-api-standart.stub +20 -0
- package/stubs/make/auth-api-switch-case.stub +38 -0
- package/stubs/make/auth-view.stub +26 -0
- package/stubs/make/class.stub +10 -2
- package/stubs/make/view.stub +3 -0
- package/stubs/service/image.stub +334 -334
- package/stubs/service/iyzico.stub +637 -637
- package/stubs/service/jwt.stub +312 -312
- package/stubs/service/phpmailer.stub +143 -143
- package/stubs/service/pusher.stub +232 -232
- package/stubs/service/qrcode.stub +169 -169
- package/stubs/service/redis.stub +361 -361
- package/stubs/service/s3.stub +377 -377
- package/stubs/service/stripe.stub +526 -526
- package/stubs/service/twilio.stub +361 -361
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@artilingo/artiframe-cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
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",
|
package/scripts/postinstall.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
20
|
+
// 2. Create Default Workspace
|
|
21
|
+
const defaultWorkspace = process.platform === 'win32'
|
|
22
|
+
? 'C:\\ArtiFrame'
|
|
23
|
+
: path.join(os.homedir(), 'ArtiFrame');
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 — 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 — 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
|
+
});
|