@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
|
@@ -0,0 +1,1892 @@
|
|
|
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 DevOps 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
|
+
* { box-sizing: border-box; }
|
|
11
|
+
body { margin: 0; font-family: 'Inter', sans-serif; background: #0d1117; color: #c9d1d9; display: flex; height: 100vh; overflow: hidden; }
|
|
12
|
+
|
|
13
|
+
/* Sidebar */
|
|
14
|
+
#sidebar { width: 260px; background: #161b22; border-right: 1px solid #30363d; display: flex; flex-direction: column; flex-shrink: 0; }
|
|
15
|
+
.brand { padding: 20px; border-bottom: 1px solid #30363d; display: flex; align-items: center; gap: 12px; }
|
|
16
|
+
.brand img { width: 32px; height: 32px; }
|
|
17
|
+
.brand-text { font-weight: 700; font-size: 16px; color: #f0f6fc; }
|
|
18
|
+
.brand-text span { color: #2ea043; }
|
|
19
|
+
|
|
20
|
+
.nav-items { flex: 1; padding: 15px 10px; display: flex; flex-direction: column; gap: 5px; overflow-y: auto; }
|
|
21
|
+
.nav-group { font-size: 11px; color: #8b949e; text-transform: uppercase; letter-spacing: 1px; font-weight: 600; margin: 15px 0 5px 10px; }
|
|
22
|
+
.nav-item { padding: 10px 15px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; gap: 10px; font-weight: 500; font-size: 14px; color: #8b949e; transition: 0.2s; }
|
|
23
|
+
.nav-item:hover { background: #21262d; color: #c9d1d9; }
|
|
24
|
+
.nav-item.active { background: #1f6feb20; color: #58a6ff; font-weight: 600; }
|
|
25
|
+
|
|
26
|
+
/* Main Area */
|
|
27
|
+
#main { flex: 1; display: flex; flex-direction: column; background: #0d1117; overflow: hidden; position: relative; }
|
|
28
|
+
.tab-content { display: none; height: 100%; flex-direction: column; }
|
|
29
|
+
.tab-content.active { display: flex; }
|
|
30
|
+
|
|
31
|
+
.tab-header { padding: 25px 30px; border-bottom: 1px solid #30363d; background: #0d1117; }
|
|
32
|
+
h2 { margin: 0; color: #f0f6fc; font-size: 22px; }
|
|
33
|
+
p.subtitle { margin: 5px 0 0 0; color: #8b949e; font-size: 14px; }
|
|
34
|
+
|
|
35
|
+
.tab-body { flex: 1; padding: 30px; overflow-y: auto; display: flex; flex-direction: column; gap: 25px; }
|
|
36
|
+
|
|
37
|
+
/* EXPLORER TAB (Yenilenmiş Tasarım) */
|
|
38
|
+
#tab-explorer .tab-header { display: none; }
|
|
39
|
+
#tab-explorer .tab-body { padding: 0; flex-direction: row; background: #0d1117; }
|
|
40
|
+
|
|
41
|
+
.explorer-tree {
|
|
42
|
+
width: 280px;
|
|
43
|
+
background: #111418;
|
|
44
|
+
border-right: 1px solid #30363d;
|
|
45
|
+
overflow-y: auto;
|
|
46
|
+
padding: 15px 10px;
|
|
47
|
+
flex-shrink: 0;
|
|
48
|
+
font-size: 13px;
|
|
49
|
+
}
|
|
50
|
+
.explorer-tree::-webkit-scrollbar { width: 8px; }
|
|
51
|
+
.explorer-tree::-webkit-scrollbar-thumb { background: #30363d; border-radius: 4px; }
|
|
52
|
+
.explorer-tree::-webkit-scrollbar-thumb:hover { background: #484f58; }
|
|
53
|
+
|
|
54
|
+
.explorer-preview {
|
|
55
|
+
flex: 1;
|
|
56
|
+
min-width: 0;
|
|
57
|
+
background: #0d1117;
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.tree-item {
|
|
63
|
+
margin-left: 0;
|
|
64
|
+
position: relative;
|
|
65
|
+
}
|
|
66
|
+
.tree-children {
|
|
67
|
+
border-left: 1px solid #30363d50;
|
|
68
|
+
margin-left: 17px;
|
|
69
|
+
padding-left: 5px;
|
|
70
|
+
margin-top: 2px;
|
|
71
|
+
margin-bottom: 2px;
|
|
72
|
+
}
|
|
73
|
+
.tree-label {
|
|
74
|
+
padding: 6px 12px;
|
|
75
|
+
border-radius: 6px;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
color: #8b949e;
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
gap: 8px;
|
|
81
|
+
user-select: none;
|
|
82
|
+
transition: all 0.15s ease;
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
text-overflow: ellipsis;
|
|
86
|
+
margin-bottom: 2px;
|
|
87
|
+
}
|
|
88
|
+
.tree-label svg { flex-shrink: 0; width: 16px; height: 16px; opacity: 0.8; }
|
|
89
|
+
.tree-label:hover { background: #1f6feb15; color: #c9d1d9; }
|
|
90
|
+
.tree-label.active { background: #1f6feb25; color: #58a6ff; font-weight: 500; }
|
|
91
|
+
|
|
92
|
+
/* Explorer Tabs (Right Pane) */
|
|
93
|
+
#explorer-deadzone-tabs > div:first-child {
|
|
94
|
+
background: #111418 !important;
|
|
95
|
+
border-bottom: 1px solid #30363d !important;
|
|
96
|
+
padding: 0 15px;
|
|
97
|
+
}
|
|
98
|
+
.exp-tab-btn {
|
|
99
|
+
background: transparent;
|
|
100
|
+
border: none;
|
|
101
|
+
color: #8b949e;
|
|
102
|
+
padding: 14px 20px;
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
font-size: 13px;
|
|
105
|
+
border-bottom: 2px solid transparent;
|
|
106
|
+
transition: all 0.2s;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
gap: 8px;
|
|
110
|
+
font-weight: 500;
|
|
111
|
+
}
|
|
112
|
+
.exp-tab-btn:hover { color: #c9d1d9; background: #ffffff05; }
|
|
113
|
+
.exp-tab-btn.active { color: #58a6ff; border-bottom-color: #58a6ff; }
|
|
114
|
+
|
|
115
|
+
/* Make Wizard Form Customization */
|
|
116
|
+
#make-wizard-form { background: #161b22 !important; border: 1px solid #30363d !important; box-shadow: 0 4px 12px rgba(0,0,0,0.2); }
|
|
117
|
+
#make-wizard-preview { background: #010409 !important; border: 1px solid #30363d !important; border-radius: 6px; }
|
|
118
|
+
|
|
119
|
+
/* Context Menu */
|
|
120
|
+
#ctx-menu { position: fixed; background: #161b22; border: 1px solid #30363d; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); padding: 5px; z-index: 1000; display: none; min-width: 220px; }
|
|
121
|
+
.ctx-item { padding: 8px 12px; font-size: 13px; color: #c9d1d9; border-radius: 4px; cursor: pointer; display: flex; align-items: center; gap: 8px; }
|
|
122
|
+
.ctx-item:hover { background: #1f6feb; color: #fff; }
|
|
123
|
+
.ctx-item.danger:hover { background: #da3633; color: #fff; }
|
|
124
|
+
.ctx-divider { height: 1px; background: #30363d; margin: 5px 0; }
|
|
125
|
+
|
|
126
|
+
/* SERVICES TAB */
|
|
127
|
+
#tab-services .tab-body { flex-direction: row; padding: 0; }
|
|
128
|
+
.services-grid-wrap { flex: 1; padding: 30px; overflow-y: auto; }
|
|
129
|
+
.services-sidebar { width: 400px; background: #161b22; border-left: 1px solid #30363d; display: flex; flex-direction: column; }
|
|
130
|
+
.services-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; }
|
|
131
|
+
.exp-tab-btn { background:transparent; border:none; color:#8b949e; padding:12px 20px; cursor:pointer; font-size:13px; border-bottom:2px solid transparent; transition:all 0.2s; }
|
|
132
|
+
.exp-tab-btn:hover { color:#c9d1d9; }
|
|
133
|
+
.exp-tab-btn.active { color:#58a6ff; border-bottom-color:#58a6ff; font-weight:600; }
|
|
134
|
+
.cron-input { font-family: 'JetBrains Mono', monospace; text-align: center; }
|
|
135
|
+
.service-card { background: #21262d; border: 1px solid #30363d; border-radius: 12px; padding: 20px; cursor: pointer; transition: 0.2s; position: relative; }
|
|
136
|
+
.service-card:hover { border-color: #58a6ff; transform: translateY(-3px); }
|
|
137
|
+
.service-card h4 { margin: 0 0 5px 0; font-size: 16px; color: #f0f6fc; }
|
|
138
|
+
.service-card .path { font-size: 12px; color: #8b949e; font-family: 'JetBrains Mono', monospace; margin-bottom: 15px; }
|
|
139
|
+
.badge { display: inline-block; padding: 4px 10px; border-radius: 20px; font-size: 11px; font-weight: 600; margin-bottom: 15px; }
|
|
140
|
+
.badge.active { background: #2ea04320; color: #3fb950; border: 1px solid #2ea04340; }
|
|
141
|
+
.badge.warning { background: #d2992220; color: #d29922; border: 1px solid #d2992240; }
|
|
142
|
+
.badge.missing { background: #30363d; color: #8b949e; border: 1px solid #484f58; }
|
|
143
|
+
.methods { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
144
|
+
.method-tag { background: #0d1117; border: 1px solid #30363d; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-family: 'JetBrains Mono', monospace; color: #a5d6ff; }
|
|
145
|
+
|
|
146
|
+
.sidebar-header { padding: 20px; border-bottom: 1px solid #30363d; display: flex; justify-content: space-between; align-items: center; background: #21262d; }
|
|
147
|
+
.sidebar-header h3 { margin: 0; font-size: 16px; color: #f0f6fc; }
|
|
148
|
+
.sidebar-content { padding: 20px; flex: 1; overflow-y: auto; }
|
|
149
|
+
|
|
150
|
+
/* UI Components */
|
|
151
|
+
.card { background: #161b22; border: 1px solid #30363d; border-radius: 12px; padding: 20px; }
|
|
152
|
+
.form-row { display: flex; gap: 20px; margin-bottom: 15px; flex-wrap: wrap; }
|
|
153
|
+
.form-group { flex: 1; display: flex; flex-direction: column; gap: 8px; min-width: 150px; }
|
|
154
|
+
.form-group label { font-size: 13px; font-weight: 600; color: #c9d1d9; }
|
|
155
|
+
.form-control { background: #0d1117; border: 1px solid #30363d; color: #f0f6fc; padding: 10px 12px; border-radius: 6px; font-size: 14px; font-family: 'Inter', sans-serif; outline: none; }
|
|
156
|
+
.form-control:focus { border-color: #58a6ff; }
|
|
157
|
+
|
|
158
|
+
.btn { padding: 10px 15px; border-radius: 6px; border: none; font-size: 14px; font-weight: 600; cursor: pointer; transition: 0.2s; width: 100%; display:inline-block; text-align:center; }
|
|
159
|
+
.btn-primary { background: #2ea043; color: white; }
|
|
160
|
+
.btn-primary:hover { background: #2c974b; }
|
|
161
|
+
.btn-danger { background: #da3633; color: white; }
|
|
162
|
+
|
|
163
|
+
.presets { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 20px; }
|
|
164
|
+
.btn-preset { background: #21262d; border: 1px solid #30363d; color: #c9d1d9; padding: 8px 15px; border-radius: 20px; font-size: 13px; cursor: pointer; }
|
|
165
|
+
.btn-preset.active { background: #1f6feb; border-color: #1f6feb; color: #fff; }
|
|
166
|
+
|
|
167
|
+
.output-box { background: #010409; border: 1px solid #30363d; border-radius: 8px; overflow: hidden; }
|
|
168
|
+
.output-header { background: #161b22; padding: 10px 15px; border-bottom: 1px solid #30363d; display: flex; justify-content: space-between; align-items: center; }
|
|
169
|
+
.output-title { font-size: 12px; font-weight: 600; color: #8b949e; text-transform: uppercase; }
|
|
170
|
+
.btn-copy { background: #2ea043; color: white; border: none; padding: 6px 12px; border-radius: 4px; font-size: 12px; font-weight: 600; cursor: pointer; }
|
|
171
|
+
.output-code { padding: 20px; margin: 0; font-family: 'JetBrains Mono', monospace; font-size: 14px; color: #a5d6ff; white-space: pre-wrap; }
|
|
172
|
+
.output-code span.keyword { color: #ff7b72; }
|
|
173
|
+
.output-code span.string { color: #a5d6ff; }
|
|
174
|
+
|
|
175
|
+
.info-card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 20px; margin-top: 5px; }
|
|
176
|
+
.info-card h4 { margin: 0 0 15px 0; color: #58a6ff; font-size: 15px; }
|
|
177
|
+
.info-card p, .info-card ul { font-size: 13px; color: #8b949e; line-height: 1.6; margin: 0 0 10px 0; }
|
|
178
|
+
.info-card code { background: #21262d; padding: 2px 6px; border-radius: 4px; color: #a5d6ff; font-family: monospace; }
|
|
179
|
+
|
|
180
|
+
.launcher-card { background: #161b22; border: 1px solid #30363d; border-radius: 12px; padding: 25px 20px; text-align: center; cursor: pointer; flex: 1; min-width: 200px; }
|
|
181
|
+
.launcher-card:hover { border-color: #58a6ff; }
|
|
182
|
+
.launcher-card.disabled { opacity: 0.5; pointer-events: none; }
|
|
183
|
+
|
|
184
|
+
.toggle-row { display: flex; align-items: center; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid #30363d; }
|
|
185
|
+
.switch { position: relative; display: inline-block; width: 44px; height: 24px; }
|
|
186
|
+
.switch input { opacity: 0; width: 0; height: 0; }
|
|
187
|
+
.slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #30363d; transition: .4s; border-radius: 34px; }
|
|
188
|
+
.slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; }
|
|
189
|
+
input:checked + .slider { background-color: #2ea043; }
|
|
190
|
+
input:checked + .slider:before { transform: translateX(20px); }
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
#modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.7); z-index: 2000; display: none; align-items: center; justify-content: center; }
|
|
199
|
+
.modal { background: #161b22; border: 1px solid #30363d; border-radius: 12px; width: 400px; padding: 25px; }
|
|
200
|
+
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; }
|
|
201
|
+
</style>
|
|
202
|
+
|
|
203
|
+
<script>
|
|
204
|
+
window.onerror = function(message, source, lineno, colno, error) {
|
|
205
|
+
require('fs').appendFileSync('/tmp/electron-errors.log', message + ' at ' + lineno + ':' + colno + '\n');
|
|
206
|
+
};
|
|
207
|
+
</script>
|
|
208
|
+
</head>
|
|
209
|
+
<body>
|
|
210
|
+
<div id="sidebar">
|
|
211
|
+
<div class="brand">
|
|
212
|
+
<img src="logo.svg" alt="Logo" onerror="this.style.display='none'">
|
|
213
|
+
<div class="brand-text">ArtiFrame <span>DevOps</span></div>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="nav-items">
|
|
216
|
+
<div class="nav-group">Araçlar</div>
|
|
217
|
+
<div class="nav-item active" onclick="switchTab('launcher')" id="nav-launcher">🚀 Başlatıcı (Launcher)</div>
|
|
218
|
+
<div class="nav-item" onclick="switchTab('explorer')" id="nav-explorer">📁 Proje Yöneticisi</div>
|
|
219
|
+
<div class="nav-item" onclick="switchTab('services')" id="nav-services">🧩 Servis Wrappers</div>
|
|
220
|
+
|
|
221
|
+
<div class="nav-group">Sistem</div>
|
|
222
|
+
<div class="nav-item" onclick="switchTab('cron')" id="nav-cron">⏱️ Cron Tasarımcısı</div>
|
|
223
|
+
<div class="nav-item" onclick="switchTab('supervise')" id="nav-supervise">🔄 Supervisor (Daemon)</div>
|
|
224
|
+
|
|
225
|
+
<div style="flex:1"></div>
|
|
226
|
+
|
|
227
|
+
<div class="card" style="margin: 10px; padding: 15px; background: #0d1117;">
|
|
228
|
+
<div style="font-size:11px; color:#8b949e; margin-bottom:5px;">AKTİF DİZİN (CWD)</div>
|
|
229
|
+
<div id="cwd-path" style="font-size:11px; font-family: 'JetBrains Mono', monospace; word-break: break-all; margin-bottom:10px;">/</div>
|
|
230
|
+
<div id="cwd-status" style="font-size: 11px; font-weight: bold; color: #ff7b72;">🔴 Proje Değil</div>
|
|
231
|
+
<div style="display:flex; gap:5px; margin-top:10px;">
|
|
232
|
+
<button class="btn" style="font-size:11px; padding:6px; flex:1; background:#21262d; border:1px solid #30363d; color:#c9d1d9; border-radius:4px;" onclick="changeWorkspace()">📂 Değiştir</button>
|
|
233
|
+
<button class="btn" style="font-size:11px; padding:6px; flex:1; background:#21262d; border:1px solid #30363d; color:#c9d1d9; border-radius:4px;" onclick="returnToWorkspace()">🏠 Workspace</button>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<div id="main">
|
|
240
|
+
<!-- LAUNCHER TAB -->
|
|
241
|
+
<div id="tab-launcher" class="tab-content active">
|
|
242
|
+
<div class="tab-header">
|
|
243
|
+
<h2>Başlatıcı (Launcher)</h2>
|
|
244
|
+
<p class="subtitle">ArtiFrame stüdyolarını ve sistem araçlarını yönetin.</p>
|
|
245
|
+
</div>
|
|
246
|
+
<div class="tab-body">
|
|
247
|
+
<div style="display:flex; gap:20px; flex-wrap:wrap;">
|
|
248
|
+
<div class="launcher-card workspace-req" onclick="openNewProjectModal()" style="display:none; background:rgba(31,111,235,0.1); border-color:#1f6feb;">
|
|
249
|
+
<div style="font-size:32px; margin-bottom:10px;">✨</div>
|
|
250
|
+
<h4 style="margin:0; color:#f0f6fc;">Yeni Proje</h4>
|
|
251
|
+
<p style="font-size:12px; color:#8b949e;">Sihirbaz ile projeyi başlat.</p>
|
|
252
|
+
</div>
|
|
253
|
+
<div class="launcher-card" onclick="launchCommand('db:start')">
|
|
254
|
+
<div style="font-size:32px; margin-bottom:10px;">🗄️</div>
|
|
255
|
+
<h4 style="margin:0; color:#f0f6fc;">DB Studio</h4>
|
|
256
|
+
<p style="font-size:12px; color:#8b949e;">Global ER tasarımcısını başlat.</p>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="launcher-card project-req" onclick="launchCommand('db:onproject')">
|
|
259
|
+
<div style="font-size:32px; margin-bottom:10px;">🔌</div>
|
|
260
|
+
<h4 style="margin:0; color:#f0f6fc;">DB onProject</h4>
|
|
261
|
+
<p style="font-size:12px; color:#8b949e;">Şemayı aktif projeye entegre et.</p>
|
|
262
|
+
</div>
|
|
263
|
+
<div class="launcher-card project-req" onclick="launchCommand('serve')">
|
|
264
|
+
<div style="font-size:32px; margin-bottom:10px;">🌐</div>
|
|
265
|
+
<h4 style="margin:0; color:#f0f6fc;">Server Studio</h4>
|
|
266
|
+
<p style="font-size:12px; color:#8b949e;">Canlı HTTP Gateway logger başlat.</p>
|
|
267
|
+
</div>
|
|
268
|
+
<div class="launcher-card project-req" onclick="launchCommand('db:export within')">
|
|
269
|
+
<div style="font-size:32px; margin-bottom:10px;">📦</div>
|
|
270
|
+
<h4 style="margin:0; color:#f0f6fc;">DB Export (Tam)</h4>
|
|
271
|
+
<p style="font-size:12px; color:#8b949e;">Yapı + Veriler (within) ile SQL yedekle.</p>
|
|
272
|
+
</div>
|
|
273
|
+
<div class="launcher-card project-req" onclick="launchCommand('db:export without')">
|
|
274
|
+
<div style="font-size:32px; margin-bottom:10px;">📄</div>
|
|
275
|
+
<h4 style="margin:0; color:#f0f6fc;">DB Export (Yapı)</h4>
|
|
276
|
+
<p style="font-size:12px; color:#8b949e;">Sadece şema (without) olarak SQL yedekle.</p>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
<div id="launcher-output" style="display:none; background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:15px; margin-top:20px; min-height:120px; max-height:300px; overflow-y:auto; line-height:1.6; white-space:pre-wrap; word-break:break-all; font-family:'JetBrains Mono', monospace;"></div>
|
|
281
|
+
|
|
282
|
+
<h3 style="color:#f0f6fc; margin:25px 0 10px 0; font-size:16px;">Sistem Kontrolleri (APP_ENV)</h3>
|
|
283
|
+
<div class="card" style="display:flex; justify-content:space-between; align-items:center;">
|
|
284
|
+
<div>
|
|
285
|
+
<h4 style="margin:0; color:#f0f6fc;" id="debug-title">Debug Modu Kapalı (Prod)</h4>
|
|
286
|
+
<p style="margin:5px 0 0 0; font-size:12px; color:#8b949e;" id="debug-desc">Şu an hatalar gizleniyor. Canlı ortam için güvenli.</p>
|
|
287
|
+
</div>
|
|
288
|
+
<button class="btn btn-primary project-req" id="btn-debug-toggle" style="width:auto;" onclick="toggleDebugMode()">Aktifleştir</button>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<!-- ENV Optimize Card -->
|
|
292
|
+
<div class="card project-req" style="display:flex; justify-content:space-between; align-items:center; margin-top:10px;">
|
|
293
|
+
<div>
|
|
294
|
+
<h4 style="margin:0; color:#f0f6fc;">.env Optimizasyonu</h4>
|
|
295
|
+
<p style="margin:5px 0 0 0; font-size:12px; color:#8b949e;">Çevre değişkenlerini önbelleğe alarak okuma performansını artırır.</p>
|
|
296
|
+
</div>
|
|
297
|
+
<button class="btn btn-primary project-req" id="btn-env-optimize" style="width:auto; min-width:120px;" onclick="optimizeEnv()">Optimize Et</button>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<!-- CLI Version Card -->
|
|
301
|
+
<div class="card" style="display:flex; justify-content:space-between; align-items:center; margin-top:10px; border-left:4px solid #1f6feb;">
|
|
302
|
+
<div>
|
|
303
|
+
<h4 style="margin:0; color:#f0f6fc;">ArtiFrame CLI Sürümü</h4>
|
|
304
|
+
<p style="margin:5px 0 0 0; font-size:12px; color:#8b949e;" id="cli-version-desc">Versiyon kontrol ediliyor...</p>
|
|
305
|
+
</div>
|
|
306
|
+
<div style="display:flex; gap:10px; align-items:center;">
|
|
307
|
+
<span id="cli-version-badge" style="font-family:monospace; color:#58a6ff; font-weight:bold; font-size:14px; background:#161b22; padding:6px 12px; border-radius:6px; border:1px solid #30363d;">...</span>
|
|
308
|
+
<button class="btn btn-danger" id="btn-cli-update" style="width:auto; display:none;" onclick="updateCli()">Güncelle</button>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<h3 style="color:#f0f6fc; margin:25px 0 10px 0; font-size:16px;">Versiyon Yönetimi (APP_VERSION)</h3>
|
|
313
|
+
<div class="card project-req">
|
|
314
|
+
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;">
|
|
315
|
+
<div>
|
|
316
|
+
<h4 style="margin:0; color:#f0f6fc;">Güncel Versiyon</h4>
|
|
317
|
+
<p style="margin:5px 0 0 0; font-size:12px; color:#8b949e;">config/app-version.php → APP_VERSION</p>
|
|
318
|
+
</div>
|
|
319
|
+
<span id="current-version-badge" style="font-size:22px; font-weight:bold; color:#58a6ff; font-family:'JetBrains Mono',monospace; background:#1f2937; padding:6px 16px; border-radius:8px; border:1px solid #30363d;">v0.0.0</span>
|
|
320
|
+
</div>
|
|
321
|
+
<div style="display:grid; grid-template-columns:1fr 1fr 1fr; gap:10px;">
|
|
322
|
+
<button class="btn" style="background:#21262d; border:1px solid #3fb950; color:#3fb950; font-size:12px;" onclick="bumpVersion('patch')">
|
|
323
|
+
⬆ Patch<br><span style="font-size:10px; opacity:0.7;">1.0.0 → 1.0.1</span>
|
|
324
|
+
</button>
|
|
325
|
+
<button class="btn" style="background:#21262d; border:1px solid #58a6ff; color:#58a6ff; font-size:12px;" onclick="bumpVersion('minor')">
|
|
326
|
+
⬆ Minor<br><span style="font-size:10px; opacity:0.7;">1.0.0 → 1.1.0</span>
|
|
327
|
+
</button>
|
|
328
|
+
<button class="btn" style="background:#21262d; border:1px solid #d2a8ff; color:#d2a8ff; font-size:12px;" onclick="bumpVersion('major')">
|
|
329
|
+
⬆ Major<br><span style="font-size:10px; opacity:0.7;">1.0.0 → 2.0.0</span>
|
|
330
|
+
</button>
|
|
331
|
+
<button class="btn" style="background:#21262d; border:1px solid #ff7b72; color:#ff7b72; font-size:12px; grid-column:1/-1;" onclick="bumpVersion('down')">
|
|
332
|
+
⬇ Downgrade (Patch)<br><span style="font-size:10px; opacity:0.7;">1.0.1 → 1.0.0</span>
|
|
333
|
+
</button>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
<!-- EXPLORER TAB -->
|
|
340
|
+
<div id="tab-explorer" class="tab-content">
|
|
341
|
+
<div class="tab-header">
|
|
342
|
+
<h2>Proje Yöneticisi (Maker & Remover)</h2>
|
|
343
|
+
<p class="subtitle">Sağ tıklayarak View, API ve Sınıfları mimari kurallara uygun şekilde üretin.</p>
|
|
344
|
+
</div>
|
|
345
|
+
<div class="tab-body">
|
|
346
|
+
<div class="explorer-tree" id="file-tree"></div>
|
|
347
|
+
<div class="explorer-preview" style="padding:0; display:flex; flex-direction:column; overflow:hidden;">
|
|
348
|
+
|
|
349
|
+
<div id="explorer-deadzone-tabs" style="display:none; flex-direction:column; height:100%;">
|
|
350
|
+
<div style="display:flex; border-bottom:1px solid #30363d; background:#0d1117;">
|
|
351
|
+
<button class="exp-tab-btn active" onclick="switchExpTab('scaffold', this)">🛠️ Make Sihirbazı</button>
|
|
352
|
+
<button class="exp-tab-btn" onclick="switchExpTab('cheat', this)">📄 Sınıf Özeti</button>
|
|
353
|
+
<button class="exp-tab-btn" onclick="switchExpTab('arch', this)">🕸️ Mimari Haritası</button>
|
|
354
|
+
<button class="exp-tab-btn" onclick="switchExpTab('api', this)">⚡ API İnspektörü</button>
|
|
355
|
+
</div>
|
|
356
|
+
<div style="flex:1; overflow-y:auto; background:#161b22; position:relative;">
|
|
357
|
+
<div id="exp-tab-scaffold" class="exp-tab-content active" style="padding:20px; display:flex; flex-direction:column; height:100%; box-sizing:border-box;">
|
|
358
|
+
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; flex-shrink:0; padding-bottom:15px; border-bottom:1px solid #30363d;">
|
|
359
|
+
<div>
|
|
360
|
+
<h3 style="color:#f0f6fc; margin:0 0 6px 0; font-size: 18px; font-weight: 600; display:flex; align-items:center; gap:8px;">
|
|
361
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#58a6ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
|
|
362
|
+
Akıllı Üretici
|
|
363
|
+
</h3>
|
|
364
|
+
<p style="color:#8b949e; font-size:13px; margin:0;" id="make-wizard-path">Klasör seçimi bekleniyor...</p>
|
|
365
|
+
</div>
|
|
366
|
+
<button class="btn btn-primary" id="make-btn-generate" onclick="executeMakeWizard()" style="display:none; width:auto; font-size:13px; font-weight:600; padding:8px 16px; border-radius:6px; box-shadow:0 4px 12px rgba(31,111,235,0.2);">
|
|
367
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:6px; vertical-align:-2px;"><path d="M12 5v14M5 12h14"/></svg>
|
|
368
|
+
Dosyayı Üret
|
|
369
|
+
</button>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
<div style="display:flex; gap:20px; flex:1; min-height:0;">
|
|
373
|
+
<!-- SOL: Form -->
|
|
374
|
+
<div id="make-wizard-form" style="width:320px; background:#161b22; border:1px solid #30363d; border-radius:8px; padding:20px; flex-shrink:0; overflow-y:auto; display:flex; flex-direction:column; gap:20px;">
|
|
375
|
+
<div style="color:#8b949e; font-size:13px; text-align:center; margin:auto;">Lütfen sol taraftaki ağaçtan bir klasöre sol tıklayın.</div>
|
|
376
|
+
</div>
|
|
377
|
+
|
|
378
|
+
<!-- SAĞ: Önizleme -->
|
|
379
|
+
<div style="flex:1; background:#010409; border:1px solid #30363d; border-radius:8px; display:flex; flex-direction:column; overflow:hidden; min-width:0; box-shadow:inset 0 0 10px rgba(0,0,0,0.5);">
|
|
380
|
+
<div style="padding:10px 15px; background:#0d1117; border-bottom:1px solid #30363d; font-size:11px; color:#8b949e; font-weight:600; letter-spacing:0.5px; display:flex; align-items:center; gap:6px;">
|
|
381
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg>
|
|
382
|
+
CANLI ÖNİZLEME
|
|
383
|
+
</div>
|
|
384
|
+
<pre id="make-wizard-preview" style="flex:1; overflow:auto; margin:0; padding:20px; font-family:'JetBrains Mono',monospace; font-size:13px; color:#e6edf3; white-space:pre; word-break:normal; line-height:1.5;"></pre>
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
<div id="exp-tab-cheat" class="exp-tab-content" style="display:none; padding:30px;">
|
|
389
|
+
<h3 style="color:#f0f6fc;">Sınıf Cheat-Sheet</h3>
|
|
390
|
+
<p style="color:#8b949e; font-size:13px;">Seçtiğiniz sınıfın metodları ve özellikleri burada yer alacak.</p>
|
|
391
|
+
</div>
|
|
392
|
+
<div id="exp-tab-arch" class="exp-tab-content" style="display:none; padding:30px;">
|
|
393
|
+
<h3 style="color:#f0f6fc;">Mimari Bağımlılık Haritası</h3>
|
|
394
|
+
<p style="color:#8b949e; font-size:13px;">Sınıfın projeyle olan ilişkileri (View, Model, Use) burada yer alacak.</p>
|
|
395
|
+
</div>
|
|
396
|
+
<div id="exp-tab-api" class="exp-tab-content" style="display:none; padding:20px; height:100%; box-sizing:border-box; flex-direction:column;">
|
|
397
|
+
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px; flex-shrink:0; border-bottom:1px solid #30363d; padding-bottom:10px;">
|
|
398
|
+
<div>
|
|
399
|
+
<h3 style="color:#f0f6fc; margin:0 0 5px 0; font-size: 18px; font-weight: 600; display:flex; align-items:center; gap:8px;">
|
|
400
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#58a6ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
|
|
401
|
+
API İnspektörü
|
|
402
|
+
</h3>
|
|
403
|
+
<p style="color:#8b949e; font-size:12px; margin:0;" id="api-inspector-path">Lütfen public/api klasöründen bir dosya seçin.</p>
|
|
404
|
+
</div>
|
|
405
|
+
<div style="display:flex; align-items:center; gap:10px;">
|
|
406
|
+
<label style="color:#8b949e; font-size:12px;">APP_URL:</label>
|
|
407
|
+
<input type="text" id="api-base-url" value="http://localhost:9002" style="background:#0d1117; border:1px solid #30363d; color:#c9d1d9; border-radius:4px; padding:4px 8px; font-size:12px; width:200px; outline:none;">
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
|
|
411
|
+
<div style="display:flex; gap:20px; flex:1; min-height:0; overflow:hidden;" id="api-inspector-container">
|
|
412
|
+
<div style="flex:1; display:flex; align-items:center; justify-content:center; color:#8b949e; font-size:13px; text-align:center;">API dosyası seçimi bekleniyor...</div>
|
|
413
|
+
</div>
|
|
414
|
+
</div>
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
|
|
418
|
+
<div id="explorer-deadzone-empty" style="flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center;">
|
|
419
|
+
<div style="font-size:48px; margin-bottom:15px; opacity:0.5;">🗂️</div>
|
|
420
|
+
<h3 style="color:#8b949e; margin:0 0 10px 0;">Proje Yönetim Merkezi</h3>
|
|
421
|
+
<p style="max-width:350px; line-height:1.6; color:#8b949e; font-size:13px;">
|
|
422
|
+
Soldaki ağaçtan bir klasöre sağ tıklayarak üretim yapın veya bir dosyaya sol tıklayarak analiz araçlarını açın.
|
|
423
|
+
</p>
|
|
424
|
+
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
</div>
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
430
|
+
|
|
431
|
+
<!-- SERVICES TAB -->
|
|
432
|
+
<div id="tab-services" class="tab-content">
|
|
433
|
+
<div class="tab-header" style="display:flex; align-items:center; justify-content:space-between;">
|
|
434
|
+
<div>
|
|
435
|
+
<h2>Servis Wrappers</h2>
|
|
436
|
+
<p class="subtitle">Harici paketleri kurun, kaldırın ve .env değişkenlerini canlı olarak düzenleyin.</p>
|
|
437
|
+
</div>
|
|
438
|
+
<input type="text" id="srv-search" class="form-control" placeholder="🔍 Servis ara..." style="width:220px; margin:0;" oninput="filterServices(this.value)">
|
|
439
|
+
</div>
|
|
440
|
+
<div class="tab-body" style="flex-direction:row; padding:0; overflow:hidden;">
|
|
441
|
+
<!-- Sol: Servis Kartları (Geniş) -->
|
|
442
|
+
<div id="srv-list" style="flex:1; overflow-y:auto; padding:30px; background:#0d1117;"></div>
|
|
443
|
+
<!-- Sağ: Detay Paneli (Daraltılmış Sidebar) -->
|
|
444
|
+
<div id="srv-detail" style="width:420px; overflow-y:auto; border-left:1px solid #30363d; padding:25px; display:none; background:#161b22; flex-direction:column;">
|
|
445
|
+
</div>
|
|
446
|
+
</div>
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
<!-- CRON TAB -->
|
|
450
|
+
<div id="tab-cron" class="tab-content">
|
|
451
|
+
<div class="tab-header">
|
|
452
|
+
<h2>Cron Tasarımcısı</h2>
|
|
453
|
+
<p class="subtitle">Zamanlanmış görevleriniz için hatasız crontab sözdizimi oluşturun.</p>
|
|
454
|
+
</div>
|
|
455
|
+
<div class="tab-body">
|
|
456
|
+
<div class="card">
|
|
457
|
+
<div class="presets">
|
|
458
|
+
<button class="btn-preset active" data-val="* * * * *">Her Dakika</button>
|
|
459
|
+
<button class="btn-preset" data-val="*/2 * * * *">2 Dakikada Bir</button>
|
|
460
|
+
<button class="btn-preset" data-val="*/5 * * * *">5 Dakikada Bir</button>
|
|
461
|
+
<button class="btn-preset" data-val="*/10 * * * *">10 Dakikada Bir</button>
|
|
462
|
+
<button class="btn-preset" data-val="*/15 * * * *">15 Dakikada Bir</button>
|
|
463
|
+
<button class="btn-preset" data-val="*/30 * * * *">Yarım Saatte Bir</button>
|
|
464
|
+
|
|
465
|
+
<button class="btn-preset" data-val="0 * * * *">Her Saat Başı</button>
|
|
466
|
+
<button class="btn-preset" data-val="0 */2 * * *">2 Saatte Bir</button>
|
|
467
|
+
<button class="btn-preset" data-val="0 */6 * * *">6 Saatte Bir</button>
|
|
468
|
+
<button class="btn-preset" data-val="0 */12 * * *">12 Saatte Bir</button>
|
|
469
|
+
|
|
470
|
+
<button class="btn-preset" data-val="0 0 * * *">Gece Yarısı (00:00)</button>
|
|
471
|
+
<button class="btn-preset" data-val="0 3 * * *">Gece (03:00) Yedekleme</button>
|
|
472
|
+
<button class="btn-preset" data-val="0 8 * * *">Sabah (08:00)</button>
|
|
473
|
+
|
|
474
|
+
<button class="btn-preset" data-val="0 0 * * 1-5">Hafta İçi (00:00)</button>
|
|
475
|
+
<button class="btn-preset" data-val="0 9 * * 1-5">Mesai Başı (Hafta İçi 09:00)</button>
|
|
476
|
+
<button class="btn-preset" data-val="0 18 * * 1-5">Mesai Sonu (Hafta İçi 18:00)</button>
|
|
477
|
+
<button class="btn-preset" data-val="0 0 * * 0,6">Hafta Sonu (00:00)</button>
|
|
478
|
+
|
|
479
|
+
<button class="btn-preset" data-val="0 0 * * 1">Haftada Bir (Pazartesi)</button>
|
|
480
|
+
<button class="btn-preset" data-val="0 0 1 * *">Ayın 1'i (Fatura Kesimi)</button>
|
|
481
|
+
<button class="btn-preset" data-val="0 0 1 1 *">Yılbaşı (1 Ocak)</button>
|
|
482
|
+
</div>
|
|
483
|
+
<div class="form-row">
|
|
484
|
+
<div class="form-group">
|
|
485
|
+
<label>Dakika</label>
|
|
486
|
+
<input type="text" class="form-control cron-input" id="c-min" value="*">
|
|
487
|
+
<small style="color:#8b949e; font-size:11px;">(0-59) *:Her, */5:5dk'da bir</small>
|
|
488
|
+
</div>
|
|
489
|
+
<div class="form-group">
|
|
490
|
+
<label>Saat</label>
|
|
491
|
+
<input type="text" class="form-control cron-input" id="c-hour" value="*">
|
|
492
|
+
</div>
|
|
493
|
+
<div class="form-group">
|
|
494
|
+
<label>Gün</label>
|
|
495
|
+
<input type="text" class="form-control cron-input" id="c-dom" value="*">
|
|
496
|
+
</div>
|
|
497
|
+
<div class="form-group">
|
|
498
|
+
<label>Ay</label>
|
|
499
|
+
<input type="text" class="form-control cron-input" id="c-month" value="*">
|
|
500
|
+
</div>
|
|
501
|
+
<div class="form-group">
|
|
502
|
+
<label>Hafta Günü</label>
|
|
503
|
+
<input type="text" class="form-control cron-input" id="c-dow" value="*">
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
506
|
+
<div class="form-group" style="margin-top:15px;">
|
|
507
|
+
<label>Çalıştırılacak Dosya Yolu (Absolute Path)</label>
|
|
508
|
+
<div style="display: flex; align-items: center; background: #0d1117; border: 1px solid #30363d; border-radius: 6px; padding: 0 12px;">
|
|
509
|
+
<span style="color: #ff7b72; font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: bold;">php</span>
|
|
510
|
+
<input type="text" id="c-cmd" value="" placeholder="/var/www/proje/job.php" style="flex: 1; background: transparent; border: none; color: #f0f6fc; padding: 12px; font-size: 14px; outline: none;">
|
|
511
|
+
<span style="color: #8b949e; font-family: 'JetBrains Mono', monospace; font-size: 13px;">>> /dev/null 2>&1</span>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
<div class="output-box">
|
|
516
|
+
<div class="output-header">
|
|
517
|
+
<div class="output-title">Üretilen Crontab Kodu</div>
|
|
518
|
+
<button class="btn-copy" onclick="copyCode('cron-output', this)">📋 Kopyala</button>
|
|
519
|
+
</div>
|
|
520
|
+
<pre class="output-code" id="cron-output"></pre>
|
|
521
|
+
</div>
|
|
522
|
+
<div class="info-card">
|
|
523
|
+
<h4>ℹ️ Cron Kılavuzu & İpuçları</h4>
|
|
524
|
+
<p style="color:#c9d1d9;"><strong>Yıldız ( * ) Ne Demektir?</strong></p>
|
|
525
|
+
<p>Yıldız karakteri, o alan için tüm olası değerleri kapsar ve <i>"Her"</i> anlamına gelir. Örneğin, 'Dakika' hanesine <code>*</code> koyarsanız, komutunuz "Her dakika" çalışır.</p>
|
|
526
|
+
|
|
527
|
+
<p style="color:#c9d1d9; margin-top:10px;"><strong>Bölü İşareti ( */sayı ) Nasıl Çalışır?</strong></p>
|
|
528
|
+
<p>Düzenli aralıklar (adımlar) belirlemek için kullanılır. Saat hanesinde <code>*/2</code> yazarsanız, "Her 2 saatte bir" (0, 2, 4, 6... saatleri) anlamına gelir. <code>*</code> işareti alanı kapsar, <code>/2</code> ise o alanı ikişer atlayarak çalıştırır.</p>
|
|
529
|
+
|
|
530
|
+
<p style="color:#c9d1d9; margin-top:10px;"><strong>Gelişmiş Seçenekler:</strong></p>
|
|
531
|
+
<ul style="padding-left:20px; color:#8b949e; font-size:13px; line-height:1.6;">
|
|
532
|
+
<li><strong>Tire (-) Aralık Belirtir:</strong> Saat hanesindeki <code>9-17</code> ifadesi, "Sadece sabah 9 ile akşam 5 arasında" çalışacağını söyler.</li>
|
|
533
|
+
<li><strong>Virgül (,) Liste Belirtir:</strong> Hafta günü hanesindeki <code>1,3,5</code> ifadesi, "Pazartesi, Çarşamba ve Cuma" anlamına gelir.</li>
|
|
534
|
+
</ul>
|
|
535
|
+
|
|
536
|
+
<hr style="border:0; border-top:1px solid #30363d; margin:15px 0;">
|
|
537
|
+
|
|
538
|
+
<p><strong>Neden <code>>> /dev/null 2>&1</code> Ekliyoruz?</strong><br>
|
|
539
|
+
Bu standart Linux yönlendirmesidir. Cron komutları her çalıştığında çıktılarını sistem postası olarak kaydetmeye çalışır ve bir süre sonra sunucu diski loglarla şişer. Bu ifade çıktıyı karadeliğe gönderir (Sessiz çalıştırır).</p>
|
|
540
|
+
</div>
|
|
541
|
+
</div>
|
|
542
|
+
</div>
|
|
543
|
+
|
|
544
|
+
<!-- SUPERVISOR TAB -->
|
|
545
|
+
<div id="tab-supervise" class="tab-content">
|
|
546
|
+
<div class="tab-header">
|
|
547
|
+
<h2>Supervisor Konfigüratörü</h2>
|
|
548
|
+
<p class="subtitle">Arka plan işçileri (queue workers, websockets) için .conf dosyası üretin.</p>
|
|
549
|
+
</div>
|
|
550
|
+
<div class="tab-body">
|
|
551
|
+
<div class="card" style="display:flex; gap:30px;">
|
|
552
|
+
<div style="flex:2; display:flex; flex-direction:column; gap:15px;">
|
|
553
|
+
<div class="form-group">
|
|
554
|
+
<label>Program Adı (Process Name)</label>
|
|
555
|
+
<input type="text" class="form-control sup-input" id="s-name" value="artiframe-worker">
|
|
556
|
+
</div>
|
|
557
|
+
<div class="form-group">
|
|
558
|
+
<label>Çalıştırılacak Komut (Command)</label>
|
|
559
|
+
<input type="text" class="form-control sup-input" id="s-cmd" value="" placeholder="Örn: php /var/www/proje/worker.php">
|
|
560
|
+
</div>
|
|
561
|
+
<div class="form-row" style="margin-bottom:0;">
|
|
562
|
+
<div class="form-group">
|
|
563
|
+
<label>Çalıştırıcı Kullanıcı (User)</label>
|
|
564
|
+
<input type="text" class="form-control sup-input" id="s-user" value="root">
|
|
565
|
+
</div>
|
|
566
|
+
<div class="form-group">
|
|
567
|
+
<label>İşlem Sayısı (numprocs)</label>
|
|
568
|
+
<input type="number" class="form-control sup-input" id="s-procs" value="1" min="1" max="64">
|
|
569
|
+
</div>
|
|
570
|
+
</div>
|
|
571
|
+
</div>
|
|
572
|
+
<div style="flex:1; border-left: 1px solid #30363d; padding-left: 30px; display:flex; flex-direction:column; justify-content:center;">
|
|
573
|
+
<div class="toggle-row">
|
|
574
|
+
<div class="toggle-label" style="font-size:13px;">Otomatik Başlat</div>
|
|
575
|
+
<label class="switch"><input type="checkbox" class="sup-input" id="s-auto" checked><span class="slider"></span></label>
|
|
576
|
+
</div>
|
|
577
|
+
<div class="toggle-row">
|
|
578
|
+
<div class="toggle-label" style="font-size:13px;">Oto. Yeniden Başlat</div>
|
|
579
|
+
<label class="switch"><input type="checkbox" class="sup-input" id="s-restart" checked><span class="slider"></span></label>
|
|
580
|
+
</div>
|
|
581
|
+
<div class="toggle-row">
|
|
582
|
+
<div class="toggle-label" style="font-size:13px;">Hataları Yönlendir</div>
|
|
583
|
+
<label class="switch"><input type="checkbox" class="sup-input" id="s-err" checked><span class="slider"></span></label>
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
</div>
|
|
587
|
+
<div class="output-box">
|
|
588
|
+
<div class="output-header">
|
|
589
|
+
<div class="output-title">/etc/supervisor/conf.d/artiframe.conf</div>
|
|
590
|
+
<button class="btn-copy" onclick="copyCode('sup-output', this)">📋 Kopyala</button>
|
|
591
|
+
</div>
|
|
592
|
+
<pre class="output-code" id="sup-output"></pre>
|
|
593
|
+
</div>
|
|
594
|
+
<div class="info-card" style="margin-top:20px;">
|
|
595
|
+
<h4>ℹ️ Supervisor Kılavuzu & İpuçları</h4>
|
|
596
|
+
<p style="color:#c9d1d9;"><strong>Supervisor Nedir?</strong></p>
|
|
597
|
+
<p>Supervisor, Linux üzerinde arka planda sürekli çalışması gereken işlemleri (queue workers, socket dinleyicileri vb.) yöneten, işlem çökerse otomatik olarak tekrar başlatan bir servis yöneticisidir.</p>
|
|
598
|
+
|
|
599
|
+
<p style="color:#c9d1d9; margin-top:10px;"><strong>Numprocs (İşlem Sayısı) Neden Önemli?</strong></p>
|
|
600
|
+
<p>Aynı komuttan aynı anda kaç adet paralel süreç (worker) çalıştırılacağını belirler. Eğer kuyruk işlemleriniz (mail gönderimi, video işleme) çok yoğunsa, bunu <code>3</code> veya <code>5</code> yaparak işleri hızlandırabilirsiniz.</p>
|
|
601
|
+
|
|
602
|
+
<p style="color:#c9d1d9; margin-top:10px;"><strong>Seçeneklerin Anlamları:</strong></p>
|
|
603
|
+
<ul style="padding-left:20px; color:#8b949e; font-size:13px; line-height:1.6;">
|
|
604
|
+
<li><strong>Otomatik Başlat (autostart):</strong> Sunucu yeniden başlatıldığında, sürecinizin de otomatik olarak ayağa kalkmasını sağlar.</li>
|
|
605
|
+
<li><strong>Oto. Yeniden Başlat (autorestart):</strong> Süreciniz bir hata yüzünden çökerse veya kapanırsa, Supervisor onu anında tekrar diriltir.</li>
|
|
606
|
+
<li><strong>Hataları Yönlendir (redirect_stderr):</strong> Hata loglarını normal loglarla birleştirir. Böylece her şeyi tek bir dosyadan kolayca takip edebilirsiniz.</li>
|
|
607
|
+
</ul>
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
</div>
|
|
614
|
+
|
|
615
|
+
<div id="ctx-menu"></div>
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
<div id="confirm-modal" style="display:none; position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:3000; align-items:center; justify-content:center;">
|
|
619
|
+
<div class="modal">
|
|
620
|
+
<h3 id="confirm-modal-title" style="color:#ff7b72; margin:0 0 10px 0;">Silme İşlemi</h3>
|
|
621
|
+
<p id="confirm-desc" style="color:#c9d1d9; font-size:13px; margin-bottom:20px; line-height:1.5;">Bu dosyayı silmek istediğinize emin misiniz?</p>
|
|
622
|
+
<div class="modal-actions">
|
|
623
|
+
<button class="btn" id="confirm-cancel" style="width:auto; background:#21262d; color:#c9d1d9;">İptal</button>
|
|
624
|
+
<button class="btn btn-danger" style="width:auto;" id="confirm-submit">Evet, Sil</button>
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
<div id="modal-overlay">
|
|
629
|
+
|
|
630
|
+
<div class="modal">
|
|
631
|
+
<h3 id="modal-title" style="color:#f0f6fc;">Dosya Oluştur</h3>
|
|
632
|
+
<p id="modal-desc" style="color:#8b949e; font-size:12px; margin-bottom:15px;">Konum</p>
|
|
633
|
+
<input type="text" id="modal-input" class="form-control" placeholder="Örn: isim.php">
|
|
634
|
+
<div class="modal-actions">
|
|
635
|
+
<button class="btn" style="width:auto; background:#21262d; color:#c9d1d9;" onclick="closeModal()">İptal</button>
|
|
636
|
+
<button class="btn btn-primary" style="width:auto;" id="modal-submit">Oluştur</button>
|
|
637
|
+
</div>
|
|
638
|
+
</div>
|
|
639
|
+
</div>
|
|
640
|
+
|
|
641
|
+
<!-- ACTION RESULT MODAL -->
|
|
642
|
+
<div id="action-result-modal" style="display:none; position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:4000; align-items:center; justify-content:center;">
|
|
643
|
+
<div class="modal" style="width:600px; max-width:90%;">
|
|
644
|
+
<h3 style="color:#3fb950; margin:0 0 10px 0;">İşlem Sonucu</h3>
|
|
645
|
+
<div id="action-result-content" style="text-align:left; background:#0d1117; border:1px solid #30363d; padding:15px; border-radius:8px; width:100%; box-sizing:border-box; font-family:'JetBrains Mono', monospace; font-size:13px; color:#c9d1d9; line-height:1.6; white-space:pre; overflow:auto; max-height:400px; margin-bottom:20px;"></div>
|
|
646
|
+
<div class="modal-actions" style="justify-content:center;">
|
|
647
|
+
<button class="btn btn-primary" style="width:120px;" onclick="document.getElementById('action-result-modal').style.display='none'">Tamam</button>
|
|
648
|
+
</div>
|
|
649
|
+
</div>
|
|
650
|
+
</div>
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
<!-- Yeni Proje Modal -->
|
|
654
|
+
<div id="new-project-modal-overlay" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.85); z-index: 3000; display: none; align-items: center; justify-content: center; backdrop-filter: blur(4px);">
|
|
655
|
+
<div class="modal" style="width:500px; padding:30px;">
|
|
656
|
+
<h3 style="color:#f0f6fc; margin-top:0;">✨ Yeni Proje Sihirbazı</h3>
|
|
657
|
+
<p style="color:#8b949e; font-size:12px; margin-bottom:20px;">ArtiFrame CLI ile yepyeni bir proje inşa ediliyor.</p>
|
|
658
|
+
|
|
659
|
+
<div id="new-project-form">
|
|
660
|
+
<div class="form-group" style="margin-bottom:12px;">
|
|
661
|
+
<label>Proje Klasör Adı</label>
|
|
662
|
+
<input type="text" id="np-folder" class="form-control" placeholder="my-app">
|
|
663
|
+
</div>
|
|
664
|
+
<div class="form-group" style="margin-bottom:12px;">
|
|
665
|
+
<label>Package Name (örn: vendor/project)</label>
|
|
666
|
+
<input type="text" id="np-package" class="form-control" placeholder="artiframe/my-app">
|
|
667
|
+
</div>
|
|
668
|
+
<div class="form-group" style="margin-bottom:12px;">
|
|
669
|
+
<label>Description</label>
|
|
670
|
+
<input type="text" id="np-desc" class="form-control" value="ArtiFrame Core PHP Application">
|
|
671
|
+
</div>
|
|
672
|
+
<div style="display:flex; gap:15px; margin-bottom:12px;">
|
|
673
|
+
<div class="form-group" style="flex:1;">
|
|
674
|
+
<label>Author Name</label>
|
|
675
|
+
<input type="text" id="np-author" class="form-control" value="Artilingo">
|
|
676
|
+
</div>
|
|
677
|
+
<div class="form-group" style="flex:1;">
|
|
678
|
+
<label>Author Homepage</label>
|
|
679
|
+
<input type="text" id="np-homepage" class="form-control" value="https://artilingo.com">
|
|
680
|
+
</div>
|
|
681
|
+
</div>
|
|
682
|
+
<div style="display:flex; gap:15px; margin-bottom:20px;">
|
|
683
|
+
<div class="form-group" style="flex:1;">
|
|
684
|
+
<label>Type</label>
|
|
685
|
+
<select id="np-type" class="form-control">
|
|
686
|
+
<option value="project" selected>project</option>
|
|
687
|
+
<option value="library">library</option>
|
|
688
|
+
<option value="composer-plugin">composer-plugin</option>
|
|
689
|
+
</select>
|
|
690
|
+
</div>
|
|
691
|
+
<div class="form-group" style="flex:1;">
|
|
692
|
+
<label>License</label>
|
|
693
|
+
<select id="np-license" class="form-control">
|
|
694
|
+
<option value="AGPL-3.0-or-later" selected>AGPL-3.0-or-later</option>
|
|
695
|
+
<option value="MIT">MIT</option>
|
|
696
|
+
<option value="Apache-2.0">Apache-2.0</option>
|
|
697
|
+
<option value="none">none</option>
|
|
698
|
+
</select>
|
|
699
|
+
</div>
|
|
700
|
+
</div>
|
|
701
|
+
|
|
702
|
+
<div class="modal-actions">
|
|
703
|
+
<button class="btn" style="width:auto; background:#21262d; color:#c9d1d9;" onclick="document.getElementById('new-project-modal-overlay').style.display='none'">İptal</button>
|
|
704
|
+
<button class="btn btn-primary" style="width:auto;" onclick="startNewProject()">Oluştur</button>
|
|
705
|
+
</div>
|
|
706
|
+
</div>
|
|
707
|
+
|
|
708
|
+
<div id="new-project-terminal" style="display:none;">
|
|
709
|
+
<div class="output-box" style="height: 300px; overflow-y:auto; background:#010409; padding:15px; border-radius:8px;">
|
|
710
|
+
<pre id="np-terminal-out" style="margin:0; font-family:'JetBrains Mono', monospace; font-size:12px; color:#a5d6ff; white-space: pre-wrap;"></pre>
|
|
711
|
+
</div>
|
|
712
|
+
<div class="modal-actions" style="margin-top:15px;">
|
|
713
|
+
<button class="btn btn-primary" id="btn-np-close" style="width:auto; display:none;" onclick="closeNewProjectAndReload()">Kapat ve Projeye Geç</button>
|
|
714
|
+
</div>
|
|
715
|
+
</div>
|
|
716
|
+
</div>
|
|
717
|
+
</div>
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
<!-- Proje Seçici Modal -->
|
|
721
|
+
<div id="project-selector-modal" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.85); z-index: 3000; display: none; align-items: center; justify-content: center; backdrop-filter: blur(4px);">
|
|
722
|
+
<div class="modal" style="width:400px; padding:30px; max-height: 80vh; display:flex; flex-direction:column;">
|
|
723
|
+
<h3 style="color:#f0f6fc; margin-top:0;">📂 Projelerim</h3>
|
|
724
|
+
<p style="color:#8b949e; font-size:12px; margin-bottom:15px;">ArtiFrame çalışma alanınızdaki projeler.</p>
|
|
725
|
+
|
|
726
|
+
<div id="project-list" style="overflow-y:auto; flex:1; display:flex; flex-direction:column; gap:8px;">
|
|
727
|
+
<!-- Projeler JS ile eklenecek -->
|
|
728
|
+
</div>
|
|
729
|
+
|
|
730
|
+
<div class="modal-actions" style="margin-top:20px;">
|
|
731
|
+
<button class="btn" style="width:100%; background:#21262d; color:#c9d1d9;" onclick="document.getElementById('project-selector-modal').style.display='none'">Kapat</button>
|
|
732
|
+
</div>
|
|
733
|
+
</div>
|
|
734
|
+
</div>
|
|
735
|
+
|
|
736
|
+
<script>
|
|
737
|
+
const { ipcRenderer } = require('electron');
|
|
738
|
+
const fs = require('fs');
|
|
739
|
+
const path = require('path');
|
|
740
|
+
const { spawn } = require('child_process');
|
|
741
|
+
|
|
742
|
+
let currentCwd = '';
|
|
743
|
+
let isProject = false;
|
|
744
|
+
let envData = {};
|
|
745
|
+
const artiframeBin = path.join(__dirname, '../bin/artiframe.php');
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
// ─── Workspace & Proje Seçimi ───────────────────────────
|
|
749
|
+
const os = require('os');
|
|
750
|
+
const defaultWorkspace = process.platform === 'win32'
|
|
751
|
+
? 'C:\\ArtiFrame'
|
|
752
|
+
: path.join(os.homedir(), 'ArtiFrame');
|
|
753
|
+
|
|
754
|
+
function returnToWorkspace() {
|
|
755
|
+
if (!fs.existsSync(defaultWorkspace)) {
|
|
756
|
+
try { fs.mkdirSync(defaultWorkspace, { recursive: true }); } catch (e) {}
|
|
757
|
+
}
|
|
758
|
+
if (currentCwd !== defaultWorkspace) {
|
|
759
|
+
initializeWorkspace(defaultWorkspace);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function changeWorkspace() {
|
|
764
|
+
const listContainer = document.getElementById('project-list');
|
|
765
|
+
listContainer.innerHTML = '';
|
|
766
|
+
|
|
767
|
+
if (!fs.existsSync(defaultWorkspace)) {
|
|
768
|
+
listContainer.innerHTML = '<div style="color:#ff7b72; font-size:13px; text-align:center;">Çalışma alanı bulunamadı.</div>';
|
|
769
|
+
} else {
|
|
770
|
+
const items = fs.readdirSync(defaultWorkspace, { withFileTypes: true });
|
|
771
|
+
let hasProjects = false;
|
|
772
|
+
|
|
773
|
+
for (let item of items) {
|
|
774
|
+
if (item.isDirectory() && !item.name.startsWith('.')) {
|
|
775
|
+
const projPath = path.join(defaultWorkspace, item.name);
|
|
776
|
+
const isProj = fs.existsSync(path.join(projPath, 'public', 'index.php')) ||
|
|
777
|
+
fs.existsSync(path.join(projPath, '.env')) ||
|
|
778
|
+
fs.existsSync(path.join(projPath, 'bin', 'artiframe.php'));
|
|
779
|
+
|
|
780
|
+
if (isProj) {
|
|
781
|
+
hasProjects = true;
|
|
782
|
+
const btn = document.createElement('button');
|
|
783
|
+
btn.className = 'btn';
|
|
784
|
+
btn.style.cssText = 'background:#161b22; border:1px solid #30363d; color:#58a6ff; text-align:left; padding:12px 15px; border-radius:6px; cursor:pointer; font-weight:bold; display:flex; align-items:center; gap:10px; transition:0.2s;';
|
|
785
|
+
btn.innerHTML = `<span style="font-size:18px;">🚀</span> <span>${item.name}</span>`;
|
|
786
|
+
btn.onmouseover = () => btn.style.borderColor = '#58a6ff';
|
|
787
|
+
btn.onmouseout = () => btn.style.borderColor = '#30363d';
|
|
788
|
+
btn.onclick = () => {
|
|
789
|
+
document.getElementById('project-selector-modal').style.display = 'none';
|
|
790
|
+
initializeWorkspace(projPath);
|
|
791
|
+
};
|
|
792
|
+
listContainer.appendChild(btn);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
if (!hasProjects) {
|
|
798
|
+
listContainer.innerHTML = '<div style="color:#8b949e; font-size:13px; text-align:center; padding:20px;">Çalışma alanında hiç proje bulunamadı.<br><br>Başlatıcı sekmesindeki sihirbazı kullanarak oluşturabilirsiniz.</div>';
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
document.getElementById('project-selector-modal').style.display = 'flex';
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
// ─── Yeni Proje Sihirbazı ──────────────────────────────
|
|
808
|
+
let npFolder = '';
|
|
809
|
+
function openNewProjectModal() {
|
|
810
|
+
document.getElementById('new-project-form').style.display = 'block';
|
|
811
|
+
document.getElementById('new-project-terminal').style.display = 'none';
|
|
812
|
+
document.getElementById('np-folder').value = '';
|
|
813
|
+
document.getElementById('np-package').value = '';
|
|
814
|
+
|
|
815
|
+
// Auto fill package name when folder changes
|
|
816
|
+
document.getElementById('np-folder').oninput = function() {
|
|
817
|
+
const val = this.value.toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
|
818
|
+
if (val) {
|
|
819
|
+
document.getElementById('np-package').value = 'artiframe/' + val;
|
|
820
|
+
} else {
|
|
821
|
+
document.getElementById('np-package').value = '';
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
document.getElementById('new-project-modal-overlay').style.display = 'flex';
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function startNewProject() {
|
|
829
|
+
npFolder = document.getElementById('np-folder').value.trim();
|
|
830
|
+
const pkg = document.getElementById('np-package').value.trim();
|
|
831
|
+
const desc = document.getElementById('np-desc').value.trim();
|
|
832
|
+
const author = document.getElementById('np-author').value.trim();
|
|
833
|
+
const homepage = document.getElementById('np-homepage').value.trim();
|
|
834
|
+
const type = document.getElementById('np-type').value;
|
|
835
|
+
const license = document.getElementById('np-license').value;
|
|
836
|
+
|
|
837
|
+
if (!npFolder) {
|
|
838
|
+
alert("Proje klasör adı gereklidir!");
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
document.getElementById('new-project-form').style.display = 'none';
|
|
843
|
+
document.getElementById('new-project-terminal').style.display = 'block';
|
|
844
|
+
const termOut = document.getElementById('np-terminal-out');
|
|
845
|
+
termOut.innerText = "Sihirbaz başlatılıyor...\n";
|
|
846
|
+
|
|
847
|
+
const proc = spawn('php', [artiframeBin, 'new', npFolder], { cwd: currentCwd });
|
|
848
|
+
|
|
849
|
+
proc.stdout.on('data', (data) => {
|
|
850
|
+
const text = data.toString();
|
|
851
|
+
termOut.innerText += text;
|
|
852
|
+
termOut.parentElement.scrollTop = termOut.parentElement.scrollHeight;
|
|
853
|
+
|
|
854
|
+
// PIPING LOGIC - Answer CLI questions automatically
|
|
855
|
+
const lowerText = text.toLowerCase();
|
|
856
|
+
|
|
857
|
+
if (lowerText.includes('package name')) {
|
|
858
|
+
proc.stdin.write(pkg + "\n");
|
|
859
|
+
}
|
|
860
|
+
else if (lowerText.includes('description') && lowerText.includes('?')) {
|
|
861
|
+
proc.stdin.write(desc + "\n");
|
|
862
|
+
}
|
|
863
|
+
else if (lowerText.includes('author name') && lowerText.includes('?')) {
|
|
864
|
+
proc.stdin.write(author + "\n");
|
|
865
|
+
}
|
|
866
|
+
else if (lowerText.includes('author homepage')) {
|
|
867
|
+
proc.stdin.write(homepage + "\n");
|
|
868
|
+
}
|
|
869
|
+
else if (lowerText.includes('type') && lowerText.includes('?')) {
|
|
870
|
+
proc.stdin.write(type + "\n");
|
|
871
|
+
}
|
|
872
|
+
else if (lowerText.includes('license') && lowerText.includes('?')) {
|
|
873
|
+
proc.stdin.write(license + "\n");
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
proc.stderr.on('data', (data) => {
|
|
878
|
+
termOut.innerText += "ERROR: " + data.toString();
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
proc.on('close', (code) => {
|
|
882
|
+
termOut.innerText += "\n\nİşlem tamamlandı! (Çıkış kodu: " + code + ")";
|
|
883
|
+
document.getElementById('btn-np-close').style.display = 'inline-block';
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function closeNewProjectAndReload() {
|
|
888
|
+
document.getElementById('new-project-modal-overlay').style.display = 'none';
|
|
889
|
+
if (npFolder) {
|
|
890
|
+
// Switch cwd to the newly created project
|
|
891
|
+
const newCwd = path.join(currentCwd, npFolder);
|
|
892
|
+
initializeWorkspace(newCwd);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
function switchTab(tabId) {
|
|
897
|
+
document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active'));
|
|
898
|
+
document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
|
|
899
|
+
document.getElementById('nav-' + tabId).classList.add('active');
|
|
900
|
+
document.getElementById('tab-' + tabId).classList.add('active');
|
|
901
|
+
|
|
902
|
+
if (tabId === 'explorer' && document.getElementById('file-tree').children.length === 0) {
|
|
903
|
+
loadTree();
|
|
904
|
+
}
|
|
905
|
+
if (tabId === 'services' && isProject) {
|
|
906
|
+
loadServices();
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function initializeWorkspace(cwd, tab = null) {
|
|
911
|
+
currentCwd = cwd;
|
|
912
|
+
document.getElementById('cwd-path').innerText = currentCwd;
|
|
913
|
+
|
|
914
|
+
const envPath = path.join(currentCwd, '.env');
|
|
915
|
+
isProject = fs.existsSync(path.join(currentCwd, 'public', 'index.php')) || fs.existsSync(envPath) || fs.existsSync(path.join(currentCwd, 'bin', 'artiframe.php'));
|
|
916
|
+
|
|
917
|
+
if (isProject) {
|
|
918
|
+
document.getElementById('cwd-status').style.color = '#3fb950';
|
|
919
|
+
document.getElementById('cwd-status').innerText = '🟢 Geçerli Proje';
|
|
920
|
+
|
|
921
|
+
document.querySelectorAll('.project-req').forEach(el => el.classList.remove('disabled'));
|
|
922
|
+
document.querySelectorAll('.workspace-req').forEach(el => el.style.display = 'none');
|
|
923
|
+
|
|
924
|
+
if (fs.existsSync(envPath)) {
|
|
925
|
+
parseEnv(envPath);
|
|
926
|
+
parseAppVersion(currentCwd);
|
|
927
|
+
updateDebugUI();
|
|
928
|
+
}
|
|
929
|
+
} else {
|
|
930
|
+
document.getElementById('cwd-status').style.color = '#58a6ff';
|
|
931
|
+
document.getElementById('cwd-status').innerText = '🔵 Çalışma Alanı (Workspace)';
|
|
932
|
+
|
|
933
|
+
document.querySelectorAll('.project-req').forEach(el => el.classList.add('disabled'));
|
|
934
|
+
document.querySelectorAll('.workspace-req').forEach(el => el.style.display = 'block');
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Always clear the file tree so it reloads for the new CWD
|
|
938
|
+
document.getElementById('file-tree').innerHTML = '';
|
|
939
|
+
|
|
940
|
+
// Re-run tab logic
|
|
941
|
+
if (tab) {
|
|
942
|
+
switchTab(tab);
|
|
943
|
+
} else {
|
|
944
|
+
// If no tab provided, re-trigger current active tab
|
|
945
|
+
const activeTab = document.querySelector('.nav-item.active');
|
|
946
|
+
if(activeTab) {
|
|
947
|
+
const tId = activeTab.id.replace('nav-', '');
|
|
948
|
+
switchTab(tId);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
ipcRenderer.on('init-data', (e, data) => {
|
|
954
|
+
initializeWorkspace(data.cwd, data.tab);
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
let appEnvMode = '0';
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
function parseEnv(envPath) {
|
|
963
|
+
envData = {};
|
|
964
|
+
try {
|
|
965
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
966
|
+
content.split('\n').forEach(line => {
|
|
967
|
+
const l = line.trim();
|
|
968
|
+
if (!l || l.startsWith('#')) return;
|
|
969
|
+
const parts = l.split('=');
|
|
970
|
+
if (parts.length >= 2) {
|
|
971
|
+
const key = parts[0].trim();
|
|
972
|
+
const val = parts.slice(1).join('=').trim().replace(/^"/, '').replace(/"$/, '');
|
|
973
|
+
envData[key] = val;
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
} catch (e) {}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
let appVersion = '0.0.0';
|
|
980
|
+
|
|
981
|
+
function parseAppVersion(projectRoot) {
|
|
982
|
+
const configPath = path.join(projectRoot, 'config', 'app-version.php');
|
|
983
|
+
if (fs.existsSync(configPath)) {
|
|
984
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
985
|
+
const envMatch = content.match(/define\(\s*'APP_ENV'\s*,\s*(0|1)\s*\);/);
|
|
986
|
+
if (envMatch) appEnvMode = envMatch[1];
|
|
987
|
+
|
|
988
|
+
const verMatch = content.match(/define\(\s*'APP_VERSION'\s*,\s*'([^']+)'\s*\);/);
|
|
989
|
+
if (verMatch) {
|
|
990
|
+
appVersion = verMatch[1];
|
|
991
|
+
const badge = document.getElementById('current-version-badge');
|
|
992
|
+
if (badge) badge.innerText = 'v' + appVersion;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function bumpVersion(type) {
|
|
998
|
+
const configPath = path.join(currentCwd, 'config', 'app-version.php');
|
|
999
|
+
if (!fs.existsSync(configPath)) return;
|
|
1000
|
+
|
|
1001
|
+
const parts = appVersion.split('.').map(Number);
|
|
1002
|
+
let [major, minor, patch] = parts;
|
|
1003
|
+
|
|
1004
|
+
if (type === 'patch') { patch++; }
|
|
1005
|
+
else if (type === 'minor') { minor++; patch = 0; }
|
|
1006
|
+
else if (type === 'major') { major++; minor = 0; patch = 0; }
|
|
1007
|
+
else if (type === 'down') {
|
|
1008
|
+
if (patch > 0) { patch--; }
|
|
1009
|
+
else if (minor > 0) { minor--; patch = 0; }
|
|
1010
|
+
else if (major > 0) { major--; minor = 0; patch = 0; }
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const newVersion = `${major}.${minor}.${patch}`;
|
|
1014
|
+
let content = fs.readFileSync(configPath, 'utf8');
|
|
1015
|
+
content = content.replace(
|
|
1016
|
+
/define\(\s*'APP_VERSION'\s*,\s*'[^']+'\s*\);/,
|
|
1017
|
+
`define('APP_VERSION', '${newVersion}');`
|
|
1018
|
+
);
|
|
1019
|
+
fs.writeFileSync(configPath, content);
|
|
1020
|
+
appVersion = newVersion;
|
|
1021
|
+
|
|
1022
|
+
const badge = document.getElementById('current-version-badge');
|
|
1023
|
+
if (badge) {
|
|
1024
|
+
badge.innerText = 'v' + newVersion;
|
|
1025
|
+
badge.style.color = type === 'down' ? '#ff7b72' : '#3fb950';
|
|
1026
|
+
setTimeout(() => { badge.style.color = '#58a6ff'; }, 1500);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
function updateDebugUI() {
|
|
1032
|
+
const isDebug = appEnvMode === '1';
|
|
1033
|
+
const btn = document.getElementById('btn-debug-toggle');
|
|
1034
|
+
const title = document.getElementById('debug-title');
|
|
1035
|
+
const desc = document.getElementById('debug-desc');
|
|
1036
|
+
|
|
1037
|
+
if (isDebug) {
|
|
1038
|
+
title.innerText = 'Debug Modu Aktif ⚠️';
|
|
1039
|
+
title.style.color = '#ff7b72';
|
|
1040
|
+
desc.innerText = 'Hata gösterimi açık. Canlı ortama bu şekilde çıkmayın!';
|
|
1041
|
+
btn.innerText = 'Kapat (Prod Yap)';
|
|
1042
|
+
btn.className = 'btn btn-danger project-req';
|
|
1043
|
+
} else {
|
|
1044
|
+
title.innerText = 'Debug Modu Kapalı (Prod) ✅';
|
|
1045
|
+
title.style.color = '#3fb950';
|
|
1046
|
+
desc.innerText = 'Canlı ortama hazır. Hatalar gizleniyor.';
|
|
1047
|
+
btn.innerText = 'Aktifleştir';
|
|
1048
|
+
btn.className = 'btn btn-primary project-req';
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
function toggleDebugMode() {
|
|
1053
|
+
const isDebug = appEnvMode === '1';
|
|
1054
|
+
const newMode = isDebug ? '0' : '1';
|
|
1055
|
+
runCliCommand(['mode', newMode], () => {
|
|
1056
|
+
parseEnv(path.join(currentCwd, '.env'));
|
|
1057
|
+
parseAppVersion(currentCwd);
|
|
1058
|
+
updateDebugUI();
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// ─── ENV Optimizasyonu ──────────────────────────────────
|
|
1063
|
+
function optimizeEnv() {
|
|
1064
|
+
const btn = document.getElementById('btn-env-optimize');
|
|
1065
|
+
const originalText = btn.innerText;
|
|
1066
|
+
btn.innerText = "İşleniyor...";
|
|
1067
|
+
btn.disabled = true;
|
|
1068
|
+
|
|
1069
|
+
const proc = require('child_process').spawn('php', [artiframeBin, 'optimize'], { cwd: currentCwd });
|
|
1070
|
+
proc.on('close', (code) => {
|
|
1071
|
+
if (code === 0) {
|
|
1072
|
+
btn.innerText = "✅ Tamamlandı";
|
|
1073
|
+
btn.style.background = "#2ea043";
|
|
1074
|
+
} else {
|
|
1075
|
+
btn.innerText = "❌ Hata";
|
|
1076
|
+
btn.style.background = "#da3633";
|
|
1077
|
+
}
|
|
1078
|
+
setTimeout(() => {
|
|
1079
|
+
btn.innerText = originalText;
|
|
1080
|
+
btn.style.background = "";
|
|
1081
|
+
btn.disabled = false;
|
|
1082
|
+
}, 2000);
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// ─── CLI Versiyon Yönetimi ──────────────────────────────
|
|
1087
|
+
let currentCliVersion = "v0.0.0";
|
|
1088
|
+
|
|
1089
|
+
function checkCliVersion() {
|
|
1090
|
+
// Run php bin/artiframe.php cli version in background
|
|
1091
|
+
const proc = require('child_process').spawn('php', [artiframeBin, 'cli version']);
|
|
1092
|
+
let output = '';
|
|
1093
|
+
|
|
1094
|
+
proc.stdout.on('data', (data) => {
|
|
1095
|
+
output += data.toString();
|
|
1096
|
+
// If the script asks "Güncellemek ister misiniz? (y/n)", we reply 'n' to just kill it gracefully
|
|
1097
|
+
if (output.toLowerCase().includes('(y/n)')) {
|
|
1098
|
+
proc.stdin.write("n\n");
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
proc.on('close', () => {
|
|
1103
|
+
// Parse the output to find VERSION_CURRENT and VERSION_LATEST
|
|
1104
|
+
// Example output:
|
|
1105
|
+
// Mevcut Versiyon: v1.0.0
|
|
1106
|
+
// En Yeni Sürüm: v1.0.1
|
|
1107
|
+
const vRegex = /v(\d+\.\d+\.\d+)/g;
|
|
1108
|
+
let matches = [];
|
|
1109
|
+
let m;
|
|
1110
|
+
while ((m = vRegex.exec(output)) !== null) {
|
|
1111
|
+
matches.push(m[0]);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
if (matches.length >= 1) {
|
|
1115
|
+
currentCliVersion = matches[0];
|
|
1116
|
+
document.getElementById('cli-version-badge').innerText = currentCliVersion;
|
|
1117
|
+
document.getElementById('cli-version-desc').innerText = "Versiyon senkronize edildi.";
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
if (matches.length >= 2) {
|
|
1121
|
+
const latestVersion = matches[1];
|
|
1122
|
+
// Compare versions
|
|
1123
|
+
if (latestVersion !== currentCliVersion && output.toLowerCase().includes('update available') || output.includes('→')) {
|
|
1124
|
+
document.getElementById('cli-version-desc').innerHTML = `<span style="color:#ff7b72; font-weight:bold;">Güncelleme Var (${latestVersion})</span>`;
|
|
1125
|
+
document.getElementById('btn-cli-update').style.display = 'block';
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function updateCli() {
|
|
1132
|
+
const btn = document.getElementById('btn-cli-update');
|
|
1133
|
+
btn.innerText = "Güncelleniyor... ⏳";
|
|
1134
|
+
btn.disabled = true;
|
|
1135
|
+
|
|
1136
|
+
const proc = require('child_process').spawn('php', [artiframeBin, 'cli version']);
|
|
1137
|
+
let output = '';
|
|
1138
|
+
|
|
1139
|
+
proc.stdout.on('data', (data) => {
|
|
1140
|
+
output += data.toString();
|
|
1141
|
+
if (output.toLowerCase().includes('(y/n)')) {
|
|
1142
|
+
proc.stdin.write("y\n");
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
proc.on('close', (code) => {
|
|
1147
|
+
btn.innerText = "✅ Güncellendi";
|
|
1148
|
+
btn.style.background = "#2ea043";
|
|
1149
|
+
setTimeout(() => {
|
|
1150
|
+
btn.style.display = 'none';
|
|
1151
|
+
btn.innerText = "Güncelle";
|
|
1152
|
+
btn.style.background = "";
|
|
1153
|
+
btn.disabled = false;
|
|
1154
|
+
checkCliVersion(); // Re-check to update badge
|
|
1155
|
+
}, 3000);
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// Initialize background check
|
|
1160
|
+
setInterval(checkCliVersion, 60 * 60 * 1000); // 60 minutes
|
|
1161
|
+
setTimeout(checkCliVersion, 1000); // Initial check after 1 second
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
// ─── Çok Adımlı Silme Sihirbazı ─────────────────────────
|
|
1165
|
+
let pendingRemovePath = '';
|
|
1166
|
+
let removeWizardState = {};
|
|
1167
|
+
|
|
1168
|
+
function searchApiUsages(apiPath, className) {
|
|
1169
|
+
let usages = [];
|
|
1170
|
+
if (!fs.existsSync(apiPath)) return usages;
|
|
1171
|
+
function scanDir(dir) {
|
|
1172
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
1173
|
+
for (let item of items) {
|
|
1174
|
+
const fullPath = path.join(dir, item.name);
|
|
1175
|
+
if (item.isDirectory()) {
|
|
1176
|
+
scanDir(fullPath);
|
|
1177
|
+
} else if (item.isFile() && item.name.endsWith('.php')) {
|
|
1178
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
1179
|
+
const lines = content.split('\n');
|
|
1180
|
+
const regex = new RegExp('\\b' + className + '\\b');
|
|
1181
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1182
|
+
if (regex.test(lines[i])) {
|
|
1183
|
+
usages.push({ file: path.relative(currentCwd, fullPath), line: i + 1 });
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
scanDir(apiPath);
|
|
1190
|
+
return usages;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
function showConfirmModal(title, bodyHtml, onYes, onNo = null) {
|
|
1194
|
+
document.getElementById('confirm-modal-title').innerText = title;
|
|
1195
|
+
document.getElementById('confirm-desc').innerHTML = bodyHtml;
|
|
1196
|
+
document.getElementById('confirm-modal').style.display = 'flex';
|
|
1197
|
+
document.getElementById('confirm-submit').onclick = () => {
|
|
1198
|
+
document.getElementById('confirm-modal').style.display = 'none';
|
|
1199
|
+
onYes();
|
|
1200
|
+
};
|
|
1201
|
+
document.getElementById('confirm-cancel').onclick = () => {
|
|
1202
|
+
document.getElementById('confirm-modal').style.display = 'none';
|
|
1203
|
+
if (onNo) onNo();
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
function confirmRemove(relPath) {
|
|
1208
|
+
pendingRemovePath = relPath;
|
|
1209
|
+
const isView = relPath.startsWith('public/') && !relPath.startsWith('public/api/') && !relPath.startsWith('public/assets/');
|
|
1210
|
+
const isClass = relPath.startsWith('src/') || relPath.startsWith('app/');
|
|
1211
|
+
|
|
1212
|
+
// ADIM 1: Ana onay
|
|
1213
|
+
showConfirmModal(
|
|
1214
|
+
'Silme İşlemi — Adım 1/1',
|
|
1215
|
+
`<strong style="color:#f0f6fc;">${relPath}</strong> dosyasını kalıcı olarak silmek istediğinize emin misiniz?`,
|
|
1216
|
+
() => {
|
|
1217
|
+
// Kullanıcı onayladı — view mi?
|
|
1218
|
+
if (isView) {
|
|
1219
|
+
const fullViewPath = path.join(currentCwd, relPath);
|
|
1220
|
+
let foundAssets = [];
|
|
1221
|
+
if (fs.existsSync(fullViewPath)) {
|
|
1222
|
+
const content = fs.readFileSync(fullViewPath, 'utf8');
|
|
1223
|
+
const cssMatch = content.match(/href="(\/assets\/css\/[^"]+\.css)(?:\?[^"]*)?"/i);
|
|
1224
|
+
if (cssMatch) {
|
|
1225
|
+
const cPath = path.join(currentCwd, 'public', cssMatch[1].replace(/^\//, ''));
|
|
1226
|
+
if (fs.existsSync(cPath)) foundAssets.push({ label: 'CSS', path: cPath });
|
|
1227
|
+
}
|
|
1228
|
+
const jsMatch = content.match(/src="(\/assets\/js\/[^"]+\.js)(?:\?[^"]*)?"/i);
|
|
1229
|
+
if (jsMatch) {
|
|
1230
|
+
const jPath = path.join(currentCwd, 'public', jsMatch[1].replace(/^\//, ''));
|
|
1231
|
+
if (fs.existsSync(jPath)) foundAssets.push({ label: 'JS', path: jPath });
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
if (foundAssets.length > 0) {
|
|
1236
|
+
// ADIM 2: Asset sorusu
|
|
1237
|
+
const assetNames = foundAssets.map(a => a.label).join(' ve ');
|
|
1238
|
+
showConfirmModal(
|
|
1239
|
+
'Varlık Silme — Adım 2/2',
|
|
1240
|
+
`Bu sayfaya ait <strong>${assetNames}</strong> varlık dosyaları bulundu.<br><br>Onlar da silinsin mi?`,
|
|
1241
|
+
() => executeRemove(relPath, { deleteAssets: true }),
|
|
1242
|
+
() => executeRemove(relPath, { deleteAssets: false })
|
|
1243
|
+
);
|
|
1244
|
+
document.getElementById('confirm-cancel').innerText = 'Hayır, Sadece PHP';
|
|
1245
|
+
document.getElementById('confirm-submit').innerText = 'Evet, Hepsini Sil';
|
|
1246
|
+
} else {
|
|
1247
|
+
executeRemove(relPath, { deleteAssets: false });
|
|
1248
|
+
}
|
|
1249
|
+
} else if (isClass) {
|
|
1250
|
+
const className = path.basename(relPath, '.php');
|
|
1251
|
+
const apiDir = path.join(currentCwd, 'public', 'api');
|
|
1252
|
+
const usages = searchApiUsages(apiDir, className);
|
|
1253
|
+
|
|
1254
|
+
if (usages.length > 0) {
|
|
1255
|
+
// ADIM 2: Sınıf kullanım uyarısı
|
|
1256
|
+
const liHtml = usages.map(u => `<li style="margin-bottom:4px;">${u.file} <span style="color:#8b949e;">(Satır: ${u.line})</span></li>`).join('');
|
|
1257
|
+
showConfirmModal(
|
|
1258
|
+
'⚠️ Kullanım Uyarısı — Adım 2/2',
|
|
1259
|
+
`<div style="background:#da363320; border:1px solid #da3633; border-radius:6px; padding:12px; margin-bottom:10px;">
|
|
1260
|
+
<div style="color:#ff7b72; font-weight:bold; margin-bottom:8px;">🚨 Bu sınıf aktif olarak kullanılıyor!</div>
|
|
1261
|
+
<ul style="margin:0; padding-left:18px; font-family:'JetBrains Mono',monospace; font-size:12px; max-height:120px; overflow-y:auto;">${liHtml}</ul>
|
|
1262
|
+
<div style="color:#ff7b72; font-size:12px; margin-top:8px;">Silerseniz ilgili API dosyaları bozulabilir!</div>
|
|
1263
|
+
</div>
|
|
1264
|
+
Yine de silmek istiyor musunuz?`,
|
|
1265
|
+
() => executeRemove(relPath, { forceClass: true }),
|
|
1266
|
+
() => updateExplorerPreview('ℹ️ Silme işlemi iptal edildi.')
|
|
1267
|
+
);
|
|
1268
|
+
document.getElementById('confirm-submit').innerText = 'Evet, Yine de Sil';
|
|
1269
|
+
} else {
|
|
1270
|
+
executeRemove(relPath, { forceClass: false });
|
|
1271
|
+
}
|
|
1272
|
+
} else {
|
|
1273
|
+
executeRemove(relPath, {});
|
|
1274
|
+
}
|
|
1275
|
+
},
|
|
1276
|
+
() => updateExplorerPreview('ℹ️ Silme işlemi iptal edildi.')
|
|
1277
|
+
);
|
|
1278
|
+
// Buton metinlerini sıfırla
|
|
1279
|
+
document.getElementById('confirm-submit').innerText = 'Evet, Sil';
|
|
1280
|
+
document.getElementById('confirm-cancel').innerText = 'İptal';
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
function executeRemove(relPath, opts) {
|
|
1284
|
+
let args = ['remove', relPath];
|
|
1285
|
+
// --force: GUI onayı zaten alındı, CLI tekrar sormasın
|
|
1286
|
+
args.push('--force');
|
|
1287
|
+
if (opts.deleteAssets === false) args.push('--no-assets');
|
|
1288
|
+
if (opts.forceClass) args.push('--force-class');
|
|
1289
|
+
|
|
1290
|
+
runCliCommand(args, (out) => {
|
|
1291
|
+
loadTree();
|
|
1292
|
+
updateExplorerPreview(out || '✅ Dosya başarıyla silindi.');
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
function runCliCommand(args, onSuccess = null) {
|
|
1297
|
+
console.log(`> artiframe ${args.join(' ')}`);
|
|
1298
|
+
const proc = spawn('php', [artiframeBin, ...args], { cwd: currentCwd });
|
|
1299
|
+
let fullOutput = '';
|
|
1300
|
+
proc.stdout.on('data', (data) => {
|
|
1301
|
+
const text = data.toString().trim();
|
|
1302
|
+
fullOutput += text + '\n';
|
|
1303
|
+
});
|
|
1304
|
+
proc.stderr.on('data', (data) => {
|
|
1305
|
+
console.error(data.toString().trim());
|
|
1306
|
+
});
|
|
1307
|
+
proc.on('close', (code) => {
|
|
1308
|
+
if (code === 0 && onSuccess) onSuccess(fullOutput);
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
const cliRoot = path.join(__dirname, '..');
|
|
1313
|
+
|
|
1314
|
+
function launchElectronApp(appDir, extraArgs = []) {
|
|
1315
|
+
const win = document.getElementById('launcher-output');
|
|
1316
|
+
win.style.display = 'block';
|
|
1317
|
+
win.innerHTML = `<div style="color:#8b949e; font-size:13px; font-family:'JetBrains Mono',monospace;">▶ Başlatılıyor: ${path.basename(appDir)}...</div>`;
|
|
1318
|
+
|
|
1319
|
+
const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
1320
|
+
const args = ['run', 'start', '--prefix', appDir, '--', ...extraArgs];
|
|
1321
|
+
const proc = spawn(npmBin, args, { cwd: appDir, detached: true, stdio: 'ignore' });
|
|
1322
|
+
proc.unref();
|
|
1323
|
+
|
|
1324
|
+
setTimeout(() => {
|
|
1325
|
+
win.innerHTML += `<div style="color:#3fb950; font-size:13px; margin-top:6px;">✅ Pencere arka planda açıldı.</div>`;
|
|
1326
|
+
}, 1000);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
function launchCommand(cmd) {
|
|
1330
|
+
const win = document.getElementById('launcher-output');
|
|
1331
|
+
win.style.display = 'block';
|
|
1332
|
+
|
|
1333
|
+
if (cmd === 'db:start') {
|
|
1334
|
+
launchElectronApp(path.join(cliRoot, 'db-studio'), [currentCwd, '--mode=offline']);
|
|
1335
|
+
} else if (cmd === 'db:onproject') {
|
|
1336
|
+
launchElectronApp(path.join(cliRoot, 'db-studio'), [currentCwd, '--mode=live']);
|
|
1337
|
+
} else if (cmd === 'serve') {
|
|
1338
|
+
// serve komutunu PHP CLI üzerinden çalıştır (kendi penceresi yok)
|
|
1339
|
+
win.innerHTML = `<div style="color:#8b949e; font-size:13px; margin-bottom:8px; font-family:'JetBrains Mono',monospace;">▶ artiframe serve...</div>`;
|
|
1340
|
+
const proc = spawn('php', [artiframeBin, 'serve'], { cwd: currentCwd });
|
|
1341
|
+
proc.stdout.on('data', (data) => {
|
|
1342
|
+
const clean = data.toString().replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
1343
|
+
win.innerHTML += `<span style="color:#c9d1d9; font-family:'JetBrains Mono',monospace; font-size:13px;">${clean}</span>`;
|
|
1344
|
+
win.scrollTop = win.scrollHeight;
|
|
1345
|
+
});
|
|
1346
|
+
proc.stderr.on('data', (data) => {
|
|
1347
|
+
const clean = data.toString().replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
1348
|
+
win.innerHTML += `<span style="color:#ff7b72; font-family:'JetBrains Mono',monospace; font-size:13px;">${clean}</span>`;
|
|
1349
|
+
win.scrollTop = win.scrollHeight;
|
|
1350
|
+
});
|
|
1351
|
+
proc.on('close', (code) => {
|
|
1352
|
+
win.innerHTML += `<div style="color:${code === 0 ? '#3fb950' : '#ff7b72'}; margin-top:8px; font-size:13px;">[${code === 0 ? 'Tamamlandı ✅' : 'Hata ❌'}]</div>`;
|
|
1353
|
+
});
|
|
1354
|
+
} else if (cmd.startsWith('db:export')) {
|
|
1355
|
+
const parts = cmd.split(' ');
|
|
1356
|
+
win.innerHTML = `<div style="color:#8b949e; font-size:13px; margin-bottom:8px; font-family:'JetBrains Mono',monospace;">▶ artiframe ${cmd}...</div>`;
|
|
1357
|
+
|
|
1358
|
+
const proc = spawn('php', [artiframeBin, parts[0], parts[1]], { cwd: currentCwd });
|
|
1359
|
+
proc.stdout.on('data', (data) => {
|
|
1360
|
+
const clean = data.toString().replace(/[][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
1361
|
+
win.innerHTML += `<span style="color:#c9d1d9; font-family:'JetBrains Mono',monospace; font-size:13px;">${clean}</span>`;
|
|
1362
|
+
win.scrollTop = win.scrollHeight;
|
|
1363
|
+
});
|
|
1364
|
+
proc.stderr.on('data', (data) => {
|
|
1365
|
+
const clean = data.toString().replace(/[][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
1366
|
+
win.innerHTML += `<span style="color:#ff7b72; font-family:'JetBrains Mono',monospace; font-size:13px;">${clean}</span>`;
|
|
1367
|
+
win.scrollTop = win.scrollHeight;
|
|
1368
|
+
});
|
|
1369
|
+
proc.on('close', (code) => {
|
|
1370
|
+
win.innerHTML += `<div style="color:${code === 0 ? '#3fb950' : '#ff7b72'}; margin-top:8px; font-size:13px;">[${code === 0 ? 'Yedekleme Tamamlandı ✅' : 'Hata ❌'}]</div>`;
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
// EXPLORER LOGIC
|
|
1377
|
+
|
|
1378
|
+
const fldIcon = `<svg viewBox="0 0 16 16" fill="currentColor" style="color:#e8a87c"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.543l-1.6-1.6A1.75 1.75 0 0 0 4.707 1H1.75ZM1.5 2.75C1.5 2.612 1.612 2.5 1.75 2.5h2.957c.23 0 .45.091.613.254l1.6 1.6A1.498 1.498 0 0 0 7.98 4.75h6.27c.138 0 .25.112.25.25v8.25a.25.25 0 0 1-.25.25H1.75a.25.25 0 0 1-.25-.25V2.75Z"></path></svg>`;
|
|
1379
|
+
const filIcon = `<svg viewBox="0 0 16 16" fill="currentColor" style="color:#58a6ff"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16H3.75A1.75 1.75 0 0 1 2 14.25V1.75Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5H3.75Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-2.938-2.938Z"></path></svg>`;
|
|
1380
|
+
|
|
1381
|
+
function loadTree() {
|
|
1382
|
+
|
|
1383
|
+
const tree = document.getElementById('file-tree');
|
|
1384
|
+
tree.innerHTML = '';
|
|
1385
|
+
['public', 'src', 'app', 'config'].forEach(folder => {
|
|
1386
|
+
const folderPath = path.join(currentCwd, folder);
|
|
1387
|
+
if (fs.existsSync(folderPath)) {
|
|
1388
|
+
// Kök dizinler için manuel node oluştur
|
|
1389
|
+
const div = document.createElement('div');
|
|
1390
|
+
div.className = 'tree-item';
|
|
1391
|
+
|
|
1392
|
+
const label = document.createElement('div');
|
|
1393
|
+
label.className = 'tree-label';
|
|
1394
|
+
label.innerHTML = fldIcon + folder;
|
|
1395
|
+
label.dataset.path = folder;
|
|
1396
|
+
label.dataset.type = 'folder';
|
|
1397
|
+
|
|
1398
|
+
const childrenContainer = document.createElement('div');
|
|
1399
|
+
childrenContainer.className = 'tree-children';
|
|
1400
|
+
childrenContainer.style.display = 'block';
|
|
1401
|
+
|
|
1402
|
+
div.appendChild(label);
|
|
1403
|
+
div.appendChild(childrenContainer);
|
|
1404
|
+
|
|
1405
|
+
label.onclick = (e) => {
|
|
1406
|
+
e.stopPropagation();
|
|
1407
|
+
|
|
1408
|
+
document.querySelectorAll('.tree-label').forEach(el => el.classList.remove('active'));
|
|
1409
|
+
label.classList.add('active');
|
|
1410
|
+
|
|
1411
|
+
const empty = document.getElementById('explorer-deadzone-empty');
|
|
1412
|
+
if (empty) empty.style.display = 'none';
|
|
1413
|
+
const tabs = document.getElementById('explorer-deadzone-tabs');
|
|
1414
|
+
if (tabs) tabs.style.display = 'flex';
|
|
1415
|
+
|
|
1416
|
+
if (typeof switchExpTab === 'function') switchExpTab('scaffold');
|
|
1417
|
+
if (typeof renderMakeWizard === 'function') renderMakeWizard(folder);
|
|
1418
|
+
|
|
1419
|
+
childrenContainer.style.display = childrenContainer.style.display === 'none' ? 'block' : 'none';
|
|
1420
|
+
if (!childrenContainer.hasChildNodes()) buildTree(folderPath, childrenContainer);
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
tree.appendChild(div);
|
|
1424
|
+
// İçeriğini doldur
|
|
1425
|
+
buildTree(folderPath, childrenContainer, false);
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
function buildTree(dirPath, parentElement, isRoot = false) {
|
|
1430
|
+
try {
|
|
1431
|
+
const items = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
1432
|
+
items.sort((a, b) => a.isDirectory() === b.isDirectory() ? a.name.localeCompare(b.name) : a.isDirectory() ? -1 : 1);
|
|
1433
|
+
items.forEach(item => {
|
|
1434
|
+
if (['vendor', 'node_modules', '.git', 'assets'].includes(item.name)) return;
|
|
1435
|
+
const fullPath = path.join(dirPath, item.name);
|
|
1436
|
+
const relPath = path.relative(currentCwd, fullPath).replace(/\\/g, '/');
|
|
1437
|
+
const div = document.createElement('div');
|
|
1438
|
+
div.className = 'tree-item';
|
|
1439
|
+
const label = document.createElement('div');
|
|
1440
|
+
label.className = 'tree-label';
|
|
1441
|
+
label.innerHTML = (item.isDirectory() ? fldIcon : filIcon) + item.name;
|
|
1442
|
+
label.dataset.path = relPath;
|
|
1443
|
+
label.dataset.type = item.isDirectory() ? 'folder' : 'file';
|
|
1444
|
+
div.appendChild(label);
|
|
1445
|
+
|
|
1446
|
+
label.onclick = (e) => {
|
|
1447
|
+
e.stopPropagation();
|
|
1448
|
+
// Active CSS yap
|
|
1449
|
+
document.querySelectorAll('.tree-label').forEach(el => el.classList.remove('active'));
|
|
1450
|
+
label.classList.add('active');
|
|
1451
|
+
// Sağ paneli aktifleştir
|
|
1452
|
+
const empty = document.getElementById('explorer-deadzone-empty');
|
|
1453
|
+
if (empty) empty.style.display = 'none';
|
|
1454
|
+
const tabs = document.getElementById('explorer-deadzone-tabs');
|
|
1455
|
+
if (tabs) tabs.style.display = 'flex';
|
|
1456
|
+
|
|
1457
|
+
if (item.isDirectory()) {
|
|
1458
|
+
switchExpTab('scaffold');
|
|
1459
|
+
if (typeof renderMakeWizard === 'function') renderMakeWizard(relPath);
|
|
1460
|
+
|
|
1461
|
+
const childrenContainer = div.querySelector('.tree-children');
|
|
1462
|
+
if (childrenContainer) {
|
|
1463
|
+
childrenContainer.style.display = childrenContainer.style.display === 'none' ? 'block' : 'none';
|
|
1464
|
+
if (!childrenContainer.hasChildNodes()) buildTree(fullPath, childrenContainer);
|
|
1465
|
+
}
|
|
1466
|
+
} else {
|
|
1467
|
+
// Dosya tıklamaları (ileride Cheat-Sheet, Mimari, API tetiklenecek)
|
|
1468
|
+
if (typeof handleFileClick === 'function') handleFileClick(relPath);
|
|
1469
|
+
}
|
|
1470
|
+
};
|
|
1471
|
+
|
|
1472
|
+
if (item.isDirectory()) {
|
|
1473
|
+
const childrenContainer = document.createElement('div');
|
|
1474
|
+
childrenContainer.className = 'tree-children';
|
|
1475
|
+
childrenContainer.style.display = isRoot ? 'block' : 'none';
|
|
1476
|
+
div.appendChild(childrenContainer);
|
|
1477
|
+
}
|
|
1478
|
+
parentElement.appendChild(div);
|
|
1479
|
+
});
|
|
1480
|
+
} catch(e) {}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
document.addEventListener('contextmenu', (e) => {
|
|
1484
|
+
const treeLabel = e.target.closest('.tree-label');
|
|
1485
|
+
if (!treeLabel) { document.getElementById('ctx-menu').style.display = 'none'; return; }
|
|
1486
|
+
e.preventDefault();
|
|
1487
|
+
showContextMenu(e.pageX, e.pageY, treeLabel.dataset.path, treeLabel.dataset.type);
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
document.addEventListener('click', () => { document.getElementById('ctx-menu').style.display = 'none'; });
|
|
1491
|
+
|
|
1492
|
+
function showContextMenu(x, y, relPath, type) {
|
|
1493
|
+
let html = '';
|
|
1494
|
+
if (type === 'folder') {
|
|
1495
|
+
html += `<div class="ctx-item" onclick="openMakeModal('folder', '${relPath}')">📁 Yeni Klasör (Folder)</div><div class="ctx-divider"></div>`;
|
|
1496
|
+
}
|
|
1497
|
+
if (relPath.startsWith('public/api')) {
|
|
1498
|
+
if (type === 'folder') {
|
|
1499
|
+
html += `<div class="ctx-item" onclick="openMakeModal('api:standart', '${relPath}')">⚡ Yeni API (Standart)</div>`;
|
|
1500
|
+
html += `<div class="ctx-item" onclick="openMakeModal('api:switch-case', '${relPath}')">🔄 Yeni API (Switch-Case)</div><div class="ctx-divider"></div>`;
|
|
1501
|
+
}
|
|
1502
|
+
} else if (relPath.startsWith('public')) {
|
|
1503
|
+
if (type === 'folder') {
|
|
1504
|
+
html += `<div class="ctx-item" onclick="openMakeModal('view', '${relPath}')">🖥️ Yeni View (Sayfa)</div><div class="ctx-divider"></div>`;
|
|
1505
|
+
}
|
|
1506
|
+
} else if (relPath.startsWith('src') || relPath.startsWith('app')) {
|
|
1507
|
+
if (type === 'folder') {
|
|
1508
|
+
html += `<div class="ctx-item" onclick="openMakeModal('class', '${relPath}')">📦 Yeni Sınıf (Class)</div><div class="ctx-divider"></div>`;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
html += `<div class="ctx-item danger" onclick="confirmRemove('${relPath}')">🗑️ Akıllı Sil (Smart Remove)</div>`;
|
|
1512
|
+
const ctxMenu = document.getElementById('ctx-menu');
|
|
1513
|
+
ctxMenu.innerHTML = html;
|
|
1514
|
+
ctxMenu.style.left = x + 'px';
|
|
1515
|
+
ctxMenu.style.top = y + 'px';
|
|
1516
|
+
ctxMenu.style.display = 'block';
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
let pendingMakeCmd = '';
|
|
1520
|
+
let pendingMakePath = '';
|
|
1521
|
+
function openMakeModal(cmd, folderPath) {
|
|
1522
|
+
pendingMakeCmd = cmd;
|
|
1523
|
+
pendingMakePath = folderPath;
|
|
1524
|
+
document.getElementById('modal-title').innerText = cmd === 'folder' ? 'Yeni Klasör' : (cmd === 'view' ? 'Yeni View' : (cmd.startsWith('api') ? 'Yeni API' : 'Yeni Sınıf'));
|
|
1525
|
+
document.getElementById('modal-desc').innerText = `Konum: ${folderPath}/`;
|
|
1526
|
+
document.getElementById('modal-input').value = '';
|
|
1527
|
+
document.getElementById('modal-overlay').style.display = 'flex';
|
|
1528
|
+
setTimeout(() => document.getElementById('modal-input').focus(), 100);
|
|
1529
|
+
}
|
|
1530
|
+
function closeModal() { document.getElementById('modal-overlay').style.display = 'none'; }
|
|
1531
|
+
function updateExplorerPreview(output) {
|
|
1532
|
+
const cleanOutput = output.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
1533
|
+
document.getElementById('action-result-content').innerText = cleanOutput;
|
|
1534
|
+
document.getElementById('action-result-modal').style.display = 'flex';
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
function switchExpTab(tabId, btnEl) {
|
|
1538
|
+
document.querySelectorAll('.exp-tab-btn').forEach(btn => btn.classList.remove('active'));
|
|
1539
|
+
document.querySelectorAll('.exp-tab-content').forEach(content => content.style.display = 'none');
|
|
1540
|
+
if (btnEl) btnEl.classList.add('active');
|
|
1541
|
+
document.getElementById('exp-tab-' + tabId).style.display = 'block';
|
|
1542
|
+
}
|
|
1543
|
+
document.getElementById('modal-submit').addEventListener('click', () => {
|
|
1544
|
+
const inputVal = document.getElementById('modal-input').value.trim();
|
|
1545
|
+
if (!inputVal) return;
|
|
1546
|
+
closeModal();
|
|
1547
|
+
|
|
1548
|
+
let safePath = pendingMakePath.replace(/\\/g, '/');
|
|
1549
|
+
|
|
1550
|
+
if (pendingMakeCmd === 'folder') {
|
|
1551
|
+
const newDirPath = path.join(currentCwd, safePath, inputVal);
|
|
1552
|
+
if (!fs.existsSync(newDirPath)) {
|
|
1553
|
+
fs.mkdirSync(newDirPath, { recursive: true });
|
|
1554
|
+
}
|
|
1555
|
+
updateExplorerPreview('Klasör başarıyla oluşturuldu:\n📁 ' + path.join(safePath, inputVal).replace(/\\/g, '/'));
|
|
1556
|
+
loadTree();
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
const handleSuccess = (output) => {
|
|
1561
|
+
loadTree();
|
|
1562
|
+
updateExplorerPreview(output);
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
if (pendingMakeCmd.startsWith('api')) {
|
|
1566
|
+
const subType = pendingMakeCmd.split(':')[1];
|
|
1567
|
+
let relativeToApi = safePath.replace(/^public\/?/, '') + '/' + inputVal;
|
|
1568
|
+
relativeToApi = relativeToApi.replace(/^\//, '').replace(/\/\//g, '/');
|
|
1569
|
+
runCliCommand(['make:api', subType, relativeToApi], handleSuccess);
|
|
1570
|
+
} else if (pendingMakeCmd === 'view') {
|
|
1571
|
+
let relativeToPublic = safePath.replace(/^public\/?/, '') + '/' + inputVal;
|
|
1572
|
+
relativeToPublic = relativeToPublic.replace(/^\//, '').replace(/\/\//g, '/');
|
|
1573
|
+
runCliCommand(['make:view', relativeToPublic], handleSuccess);
|
|
1574
|
+
} else if (pendingMakeCmd === 'class') {
|
|
1575
|
+
let fullPathArg = safePath + '/' + inputVal;
|
|
1576
|
+
fullPathArg = fullPathArg.replace(/\/\//g, '/');
|
|
1577
|
+
runCliCommand(['make:class', fullPathArg], handleSuccess);
|
|
1578
|
+
}
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
// SERVICES LOGIC
|
|
1582
|
+
const servicesDef = [
|
|
1583
|
+
// ── Ödeme ──────────────────────────────────────────────
|
|
1584
|
+
{ id: 'iyzico', name: 'Iyzico Payment', icon: '💳', pkg: 'iyzipay/iyzipay-php', class: 'src/Service/Iyzico.php', keys: ['IYZICO_API_KEY', 'IYZICO_SECRET_KEY', 'IYZICO_BASE_URL'], methods: ['pay()', 'threeDSecureInit()'] },
|
|
1585
|
+
{ id: 'stripe', name: 'Stripe Payments', icon: '💳', pkg: 'stripe/stripe-php', class: 'src/Service/Stripe.php', keys: ['STRIPE_SECRET_KEY'], methods: ['createPaymentIntent()', 'webhook()'] },
|
|
1586
|
+
// ── Auth / Güvenlik ────────────────────────────────────
|
|
1587
|
+
{ id: 'jwt', name: 'JWT Auth', icon: '🔑', pkg: 'firebase/php-jwt', class: 'src/Auth/JwtAuth.php', keys: ['JWT_SECRET_KEY', 'JWT_ALGORITHM', 'JWT_EXPIRY'], methods: ['encode()', 'decode()'] },
|
|
1588
|
+
// ── Depolama ───────────────────────────────────────────
|
|
1589
|
+
{ id: 's3', name: 'AWS S3 Storage', icon: '☁️', pkg: 'aws/aws-sdk-php', class: 'src/Service/S3Storage.php', keys: ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_DEFAULT_REGION', 'AWS_BUCKET', 'AWS_ENDPOINT'], methods: ['upload()', 'delete()', 'url()'] },
|
|
1590
|
+
// ── Görüntü ────────────────────────────────────────────
|
|
1591
|
+
{ id: 'image', name: 'Image Processor', icon: '🖼️', pkg: 'intervention/image', class: 'src/Service/ImageProcessor.php', keys: ['IMAGE_DRIVER', 'IMAGE_QUALITY'], methods: ['resize()', 'crop()', 'convert()'] },
|
|
1592
|
+
// ── Önbellek / Realtime ────────────────────────────────
|
|
1593
|
+
{ id: 'redis', name: 'Redis Cache', icon: '⚡', pkg: 'predis/predis', class: 'src/Service/RedisCache.php', keys: ['REDIS_HOST', 'REDIS_PORT', 'REDIS_PASSWORD', 'REDIS_DATABASE'], methods: ['get()', 'set()', 'del()'] },
|
|
1594
|
+
{ id: 'pusher', name: 'Pusher Realtime', icon: '📡', pkg: 'pusher/pusher-php-server', class: 'src/Service/Pusher.php', keys: ['PUSHER_APP_ID', 'PUSHER_APP_KEY', 'PUSHER_APP_SECRET', 'PUSHER_CLUSTER'], methods: ['trigger()'] },
|
|
1595
|
+
// ── İletişim ───────────────────────────────────────────
|
|
1596
|
+
{ id: 'phpmailer', name: 'PHPMailer', icon: '📧', pkg: 'phpmailer/phpmailer', class: 'src/Service/Mailer.php', keys: ['MAIL_HOST', 'MAIL_PORT', 'MAIL_USERNAME', 'MAIL_PASSWORD', 'MAIL_FROM_ADDRESS'], methods: ['send()'] },
|
|
1597
|
+
{ id: 'twilio', name: 'Twilio SMS', icon: '📱', pkg: 'twilio/sdk', class: 'src/Service/Twilio.php', keys: ['TWILIO_SID', 'TWILIO_AUTH_TOKEN', 'TWILIO_FROM_NUMBER'], methods: ['sendSms()', 'call()'] },
|
|
1598
|
+
// ── İzleme / Loglama ───────────────────────────────────
|
|
1599
|
+
{ id: 'sentry', name: 'Sentry Monitoring', icon: '🐛', pkg: 'sentry/sentry', class: 'src/Service/Sentry.php', keys: ['SENTRY_DSN'], methods: ['captureMessage()', 'captureException()'] },
|
|
1600
|
+
{ id: 'monolog', name: 'Monolog Logger', icon: '📋', pkg: 'monolog/monolog', class: null, keys: [], methods: ['info()', 'error()', 'debug()'] },
|
|
1601
|
+
// ── PDF / Belge ────────────────────────────────────────
|
|
1602
|
+
{ id: 'mpdf', name: 'mPDF', icon: '📄', pkg: 'mpdf/mpdf', class: null, keys: [], methods: ['writeHTML()', 'Output()'] },
|
|
1603
|
+
{ id: 'dompdf', name: 'DomPDF', icon: '📄', pkg: 'dompdf/dompdf', class: null, keys: [], methods: ['loadHtml()', 'render()'] },
|
|
1604
|
+
{ id: 'spreadsheet', name: 'PHPSpreadsheet', icon: '📊', pkg: 'phpoffice/phpspreadsheet', class: null, keys: [], methods: ['createSheet()', 'save()'] },
|
|
1605
|
+
// ── QR / HTTP / Yardımcı ───────────────────────────────
|
|
1606
|
+
{ id: 'qrcode', name: 'QR Code', icon: '🔲', pkg: 'endroid/qr-code', class: 'src/Service/QrCode.php', keys: [], methods: ['generate()'] },
|
|
1607
|
+
{ id: 'guzzle', name: 'Guzzle HTTP', icon: '🌐', pkg: 'guzzlehttp/guzzle', class: null, keys: [], methods: ['get()', 'post()', 'request()'] },
|
|
1608
|
+
{ id: 'uuid', name: 'UUID Generator', icon: '🆔', pkg: 'ramsey/uuid', class: null, keys: [], methods: ['uuid4()', 'uuid1()'] },
|
|
1609
|
+
{ id: 'carbon', name: 'Carbon Date', icon: '📅', pkg: 'nesbot/carbon', class: null, keys: [], methods: ['now()', 'parse()', 'diffForHumans()'] },
|
|
1610
|
+
];
|
|
1611
|
+
|
|
1612
|
+
// ─── Servis Kategorileri ──────────────────────────────────
|
|
1613
|
+
const srvCategories = [
|
|
1614
|
+
{ label: '💳 Ödeme', ids: ['iyzico', 'stripe'] },
|
|
1615
|
+
{ label: '🔑 Auth & Güvenlik', ids: ['jwt'] },
|
|
1616
|
+
{ label: '☁️ Depolama', ids: ['s3'] },
|
|
1617
|
+
{ label: '🖼️ Görüntü', ids: ['image'] },
|
|
1618
|
+
{ label: '⚡ Önbellek & Realtime',ids: ['redis', 'pusher'] },
|
|
1619
|
+
{ label: '📧 İletişim', ids: ['phpmailer', 'twilio'] },
|
|
1620
|
+
{ label: '🐛 İzleme & Log', ids: ['sentry', 'monolog'] },
|
|
1621
|
+
{ label: '📄 PDF & Belge', ids: ['mpdf', 'dompdf', 'spreadsheet'] },
|
|
1622
|
+
{ label: '🔧 Araçlar', ids: ['qrcode', 'guzzle', 'uuid', 'carbon'] },
|
|
1623
|
+
];
|
|
1624
|
+
|
|
1625
|
+
let activeSrvId = null;
|
|
1626
|
+
|
|
1627
|
+
function isComposerInstalled(pkg) {
|
|
1628
|
+
const lockFile = path.join(currentCwd, 'composer.lock');
|
|
1629
|
+
if (!fs.existsSync(lockFile)) return false;
|
|
1630
|
+
return fs.readFileSync(lockFile, 'utf8').includes('"' + pkg + '"');
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
function getSrvStatus(srv) {
|
|
1634
|
+
const composerOk = isComposerInstalled(srv.pkg);
|
|
1635
|
+
if (!composerOk) return { code: 'not-installed', label: 'Kurulu Değil', color: '#8b949e' };
|
|
1636
|
+
if (srv.class && !fs.existsSync(path.join(currentCwd, srv.class))) {
|
|
1637
|
+
return { code: 'no-class', label: 'Paket Var, Servis Sınıfı Yok', color: '#e3b341' };
|
|
1638
|
+
}
|
|
1639
|
+
if (srv.keys.length > 0) {
|
|
1640
|
+
const missing = srv.keys.filter(k => !envData[k] || envData[k].trim() === '');
|
|
1641
|
+
if (missing.length > 0) return { code: 'missing-env', label: `Eksik .env (${missing.length} anahtar)`, color: '#e3b341' };
|
|
1642
|
+
}
|
|
1643
|
+
return { code: 'ok', label: 'Kurulu & Yapılandırıldı ✅', color: '#3fb950' };
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
function loadServices() {
|
|
1647
|
+
parseEnv(path.join(currentCwd, '.env'));
|
|
1648
|
+
renderSrvList(servicesDef);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
function renderSrvList(list) {
|
|
1652
|
+
const panel = document.getElementById('srv-list');
|
|
1653
|
+
panel.innerHTML = '';
|
|
1654
|
+
|
|
1655
|
+
srvCategories.forEach(cat => {
|
|
1656
|
+
const catItems = cat.ids
|
|
1657
|
+
.map(id => list.find(s => s.id === id))
|
|
1658
|
+
.filter(Boolean);
|
|
1659
|
+
if (catItems.length === 0) return;
|
|
1660
|
+
|
|
1661
|
+
const catHeader = document.createElement('h3');
|
|
1662
|
+
catHeader.style = 'font-size:16px; font-weight:600; color:#f0f6fc; margin:15px 0 10px 0; border-bottom:1px solid #30363d; padding-bottom:8px;';
|
|
1663
|
+
catHeader.innerText = cat.label;
|
|
1664
|
+
panel.appendChild(catHeader);
|
|
1665
|
+
|
|
1666
|
+
const grid = document.createElement('div');
|
|
1667
|
+
grid.style = 'display:grid; grid-template-columns:repeat(auto-fill, minmax(280px, 1fr)); gap:15px; margin-bottom:25px;';
|
|
1668
|
+
|
|
1669
|
+
catItems.forEach(srv => {
|
|
1670
|
+
const st = getSrvStatus(srv);
|
|
1671
|
+
const item = document.createElement('div');
|
|
1672
|
+
item.className = 'service-card';
|
|
1673
|
+
item.style = `position:relative; background:${activeSrvId === srv.id ? '#1f2937' : '#161b22'}; border:1px solid ${activeSrvId === srv.id ? '#58a6ff' : '#30363d'}; border-radius:8px; padding:15px; cursor:pointer; transition:all 0.2s;`;
|
|
1674
|
+
|
|
1675
|
+
item.innerHTML = `
|
|
1676
|
+
<div style="display:flex; align-items:center; gap:12px; margin-bottom:10px;">
|
|
1677
|
+
<div style="font-size:28px;">${srv.icon || '🔌'}</div>
|
|
1678
|
+
<div>
|
|
1679
|
+
<h4 style="margin:0; font-size:15px; color:#f0f6fc;">${srv.name}</h4>
|
|
1680
|
+
<div style="font-family:'JetBrains Mono',monospace; font-size:11px; color:#8b949e; margin-top:3px;">${srv.pkg}</div>
|
|
1681
|
+
</div>
|
|
1682
|
+
</div>
|
|
1683
|
+
<div style="font-size:12px; font-weight:500; color:${st.color}; background:#0d1117; padding:4px 8px; border-radius:4px; display:inline-block; border:1px solid #30363d;">
|
|
1684
|
+
${st.label}
|
|
1685
|
+
</div>
|
|
1686
|
+
`;
|
|
1687
|
+
|
|
1688
|
+
item.onclick = () => { activeSrvId = srv.id; renderSrvList(list); showSrvDetail(srv); };
|
|
1689
|
+
item.onmouseenter = () => { if (activeSrvId !== srv.id) item.style.borderColor = '#8b949e'; };
|
|
1690
|
+
item.onmouseleave = () => { if (activeSrvId !== srv.id) item.style.borderColor = '#30363d'; };
|
|
1691
|
+
|
|
1692
|
+
grid.appendChild(item);
|
|
1693
|
+
});
|
|
1694
|
+
panel.appendChild(grid);
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
function filterServices(query) {
|
|
1699
|
+
const q = query.toLowerCase();
|
|
1700
|
+
const filtered = q ? servicesDef.filter(s => s.name.toLowerCase().includes(q) || s.id.includes(q) || s.pkg.includes(q)) : servicesDef;
|
|
1701
|
+
renderSrvList(filtered);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
function showSrvDetail(srv) {
|
|
1705
|
+
const st = getSrvStatus(srv);
|
|
1706
|
+
const panel = document.getElementById('srv-detail');
|
|
1707
|
+
panel.style.display = 'block';
|
|
1708
|
+
panel.style.alignItems = 'flex-start';
|
|
1709
|
+
panel.style.justifyContent = 'flex-start';
|
|
1710
|
+
|
|
1711
|
+
const isInstalled = st.code !== 'not-installed';
|
|
1712
|
+
|
|
1713
|
+
let envHtml = '';
|
|
1714
|
+
if (srv.keys.length > 0) {
|
|
1715
|
+
const envRows = srv.keys.map(k => {
|
|
1716
|
+
const val = (envData[k] || '').replace(/"/g, '"');
|
|
1717
|
+
const missing = !envData[k] || envData[k].trim() === '';
|
|
1718
|
+
return `<div style="margin-bottom:12px;">
|
|
1719
|
+
<label style="display:block; font-size:11px; color:${missing ? '#e3b341' : '#8b949e'}; margin-bottom:4px; font-family:'JetBrains Mono',monospace;">${k}${missing ? ' ⚠️' : ''}</label>
|
|
1720
|
+
<input type="text" class="form-control env-live-input" data-key="${k}" value="${val}" placeholder="Boş" style="font-family:'JetBrains Mono',monospace; font-size:13px;">
|
|
1721
|
+
</div>`;
|
|
1722
|
+
}).join('');
|
|
1723
|
+
|
|
1724
|
+
envHtml = `
|
|
1725
|
+
<div style="background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:18px; margin-bottom:20px;">
|
|
1726
|
+
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:14px;">
|
|
1727
|
+
<span style="font-size:12px; font-weight:bold; color:#8b949e; text-transform:uppercase; letter-spacing:0.05em;">.env Parametreleri</span>
|
|
1728
|
+
<button class="btn btn-primary" style="width:auto; font-size:12px; padding:5px 14px;" onclick="saveSrvEnv('${srv.id}')">💾 Kaydet</button>
|
|
1729
|
+
</div>
|
|
1730
|
+
${envRows}
|
|
1731
|
+
</div>`;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
let actionsHtml = '';
|
|
1735
|
+
if (!isInstalled) {
|
|
1736
|
+
actionsHtml = `<button class="btn btn-primary" style="width:100%;" onclick="installService('${srv.id}')">📦 Kur (artiframe add ${srv.id})</button>`;
|
|
1737
|
+
} else {
|
|
1738
|
+
actionsHtml = `
|
|
1739
|
+
<div style="display:flex; gap:10px;">
|
|
1740
|
+
<button class="btn" style="flex:1; background:#21262d; border:1px solid #30363d; color:#c9d1d9;" onclick="reinstallService('${srv.id}')">🔄 Yeniden Kur</button>
|
|
1741
|
+
<button class="btn btn-danger" style="flex:1;" onclick="removeService('${srv.id}', '${srv.pkg}')">🗑️ Kaldır</button>
|
|
1742
|
+
</div>`;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
panel.innerHTML = `
|
|
1746
|
+
<div style="width:100%;">
|
|
1747
|
+
<!-- Başlık -->
|
|
1748
|
+
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; padding-bottom:15px; border-bottom:1px solid #30363d;">
|
|
1749
|
+
<div style="display:flex; align-items:center; gap:12px;">
|
|
1750
|
+
<span style="font-size:32px;">${srv.icon || '🔌'}</span>
|
|
1751
|
+
<div>
|
|
1752
|
+
<h2 style="margin:0; font-size:18px; color:#f0f6fc;">${srv.name}</h2>
|
|
1753
|
+
<div style="font-family:'JetBrains Mono',monospace; font-size:11px; color:#8b949e; margin-top:2px;">${srv.pkg}</div>
|
|
1754
|
+
</div>
|
|
1755
|
+
</div>
|
|
1756
|
+
<span style="cursor:pointer; color:#8b949e; font-size:20px;" onclick="document.getElementById('srv-detail').style.display='none'; activeSrvId=null; renderSrvList(servicesDef);">×</span>
|
|
1757
|
+
</div>
|
|
1758
|
+
|
|
1759
|
+
<div style="font-size:12px; margin-bottom:20px; padding:6px 10px; border-radius:6px; background:#0d1117; border:1px solid ${st.color}; color:${st.color}; text-align:center;">
|
|
1760
|
+
${st.label}
|
|
1761
|
+
</div>
|
|
1762
|
+
|
|
1763
|
+
<!-- Servis sınıfı + Metodlar -->
|
|
1764
|
+
${srv.class ? `
|
|
1765
|
+
<div style="background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:14px; margin-bottom:20px; display:flex; flex-direction:column; gap:10px;">
|
|
1766
|
+
<div>
|
|
1767
|
+
<div style="font-size:11px; color:#8b949e; margin-bottom:4px;">SERVİS SINIFI</div>
|
|
1768
|
+
<div style="font-family:'JetBrains Mono',monospace; font-size:12px; color:#58a6ff;">${srv.class}</div>
|
|
1769
|
+
</div>
|
|
1770
|
+
<div>
|
|
1771
|
+
<div style="font-size:11px; color:#8b949e; margin-bottom:4px;">METODLAR</div>
|
|
1772
|
+
<div style="display:flex; gap:6px; flex-wrap:wrap;">
|
|
1773
|
+
${srv.methods.map(m => `<span style="font-size:11px; background:#21262d; border:1px solid #30363d; padding:2px 8px; border-radius:4px; font-family:'JetBrains Mono',monospace; color:#c9d1d9;">${m}</span>`).join('')}
|
|
1774
|
+
</div>
|
|
1775
|
+
</div>
|
|
1776
|
+
</div>` : ''}
|
|
1777
|
+
|
|
1778
|
+
<!-- .env editörü -->
|
|
1779
|
+
${envHtml}
|
|
1780
|
+
|
|
1781
|
+
<!-- Eylemler -->
|
|
1782
|
+
<div id="srv-action-output-${srv.id}" style="display:none; background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:14px; margin-bottom:14px; font-family:'JetBrains Mono',monospace; font-size:12px; max-height:150px; overflow-y:auto; line-height:1.6;"></div>
|
|
1783
|
+
${actionsHtml}
|
|
1784
|
+
</div>`;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
function saveSrvEnv(srvId) {
|
|
1788
|
+
const envPath = path.join(currentCwd, '.env');
|
|
1789
|
+
if (!fs.existsSync(envPath)) return;
|
|
1790
|
+
|
|
1791
|
+
let content = fs.readFileSync(envPath, 'utf8');
|
|
1792
|
+
document.querySelectorAll('.env-live-input').forEach(input => {
|
|
1793
|
+
const key = input.dataset.key;
|
|
1794
|
+
const val = input.value;
|
|
1795
|
+
const regex = new RegExp(`^(${key}=).*$`, 'm');
|
|
1796
|
+
if (regex.test(content)) {
|
|
1797
|
+
content = content.replace(regex, `$1${val}`);
|
|
1798
|
+
} else {
|
|
1799
|
+
content += `\n${key}=${val}`;
|
|
1800
|
+
}
|
|
1801
|
+
});
|
|
1802
|
+
fs.writeFileSync(envPath, content);
|
|
1803
|
+
parseEnv(envPath);
|
|
1804
|
+
renderSrvList(servicesDef);
|
|
1805
|
+
// Kısa onay mesajı
|
|
1806
|
+
const btn = document.querySelector(`button[onclick="saveSrvEnv('${srvId}')"]`);
|
|
1807
|
+
if (btn) { btn.innerText = '✅ Kaydedildi'; setTimeout(() => { btn.innerText = '💾 Kaydet'; }, 1500); }
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
function installService(srvId) {
|
|
1811
|
+
const out = document.getElementById('srv-action-output-' + srvId);
|
|
1812
|
+
if (out) { out.style.display = 'block'; out.innerHTML = `<span style="color:#8b949e;">▶ artiframe add ${srvId}...</span>\n`; }
|
|
1813
|
+
const proc = require('child_process').spawn('php', [artiframeBin, 'add', srvId], { cwd: currentCwd });
|
|
1814
|
+
proc.stdout.on('data', d => { if (out) { out.innerHTML += d.toString(); out.scrollTop = out.scrollHeight; } });
|
|
1815
|
+
proc.stderr.on('data', d => { if (out) { out.innerHTML += `<span style="color:#8b949e;">${d.toString()}</span>`; out.scrollTop = out.scrollHeight; } });
|
|
1816
|
+
proc.on('close', () => {
|
|
1817
|
+
loadServices();
|
|
1818
|
+
const srv = servicesDef.find(s => s.id === srvId);
|
|
1819
|
+
if (srv) showSrvDetail(srv);
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
function reinstallService(srvId) { installService(srvId); }
|
|
1824
|
+
|
|
1825
|
+
function removeService(srvId, pkg) {
|
|
1826
|
+
const out = document.getElementById('srv-action-output-' + srvId);
|
|
1827
|
+
if (out) { out.style.display = 'block'; out.innerHTML = `<span style="color:#ff7b72;">▶ composer remove ${pkg}...</span>\n`; }
|
|
1828
|
+
const proc = require('child_process').spawn('composer', ['remove', pkg], { cwd: currentCwd });
|
|
1829
|
+
proc.stdout.on('data', d => { if (out) out.innerHTML += d.toString(); out.scrollTop = out.scrollHeight; });
|
|
1830
|
+
proc.stderr.on('data', d => { if (out) out.innerHTML += `<span style="color:#8b949e;">${d.toString()}</span>`; out.scrollTop = out.scrollHeight; });
|
|
1831
|
+
proc.on('close', () => { loadServices(); const srv = servicesDef.find(s => s.id === srvId); if (srv) showSrvDetail(srv); });
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// Eski uyumluluk boşlukları
|
|
1835
|
+
function closeServiceSidebar() {}
|
|
1836
|
+
function showServiceDetails() {}
|
|
1837
|
+
|
|
1838
|
+
// CRON & SUP LOGIC
|
|
1839
|
+
const cronInputs = ['c-min', 'c-hour', 'c-dom', 'c-month', 'c-dow'];
|
|
1840
|
+
const cronCmd = document.getElementById('c-cmd');
|
|
1841
|
+
const cronOutput = document.getElementById('cron-output');
|
|
1842
|
+
function updateCronOutput() {
|
|
1843
|
+
const parts = cronInputs.map(id => document.getElementById(id).value.trim() || '*');
|
|
1844
|
+
const file = cronCmd.value.trim() || '[DOSYA_YOLU]';
|
|
1845
|
+
const cmd = 'php ' + file + ' >> /dev/null 2>&1';
|
|
1846
|
+
cronOutput.innerHTML = `<span class="keyword">${parts.join(' ')}</span> <span class="string">${cmd}</span>`;
|
|
1847
|
+
}
|
|
1848
|
+
cronInputs.forEach(id => document.getElementById(id).addEventListener('input', () => {
|
|
1849
|
+
document.querySelectorAll('.btn-preset').forEach(b => b.classList.remove('active'));
|
|
1850
|
+
updateCronOutput();
|
|
1851
|
+
}));
|
|
1852
|
+
cronCmd.addEventListener('input', updateCronOutput);
|
|
1853
|
+
document.querySelectorAll('.btn-preset').forEach(btn => {
|
|
1854
|
+
btn.addEventListener('click', (e) => {
|
|
1855
|
+
document.querySelectorAll('.btn-preset').forEach(b => b.classList.remove('active'));
|
|
1856
|
+
e.target.classList.add('active');
|
|
1857
|
+
const parts = e.target.getAttribute('data-val').split(' ');
|
|
1858
|
+
cronInputs.forEach((id, idx) => { document.getElementById(id).value = parts[idx]; });
|
|
1859
|
+
updateCronOutput();
|
|
1860
|
+
});
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
const supInputs = document.querySelectorAll('.sup-input');
|
|
1864
|
+
const supOutput = document.getElementById('sup-output');
|
|
1865
|
+
function updateSupOutput() {
|
|
1866
|
+
const name = document.getElementById('s-name').value.trim() || 'worker';
|
|
1867
|
+
const cmd = document.getElementById('s-cmd').value.trim();
|
|
1868
|
+
const user = document.getElementById('s-user').value.trim() || 'root';
|
|
1869
|
+
const procs = document.getElementById('s-procs').value || 1;
|
|
1870
|
+
const autostart = document.getElementById('s-auto').checked ? 'true' : 'false';
|
|
1871
|
+
const autorestart = document.getElementById('s-restart').checked ? 'true' : 'false';
|
|
1872
|
+
const redirErr = document.getElementById('s-err').checked ? 'true' : 'false';
|
|
1873
|
+
supOutput.innerHTML = `<span class="keyword">[program:${name}]</span>\n<span class="string">process_name</span>=%(program_name)s_%(process_num)02d\n<span class="string">command</span>=${cmd}\n<span class="string">autostart</span>=${autostart}\n<span class="string">autorestart</span>=${autorestart}\n<span class="string">user</span>=${user}\n<span class="string">numprocs</span>=${procs}\n<span class="string">redirect_stderr</span>=${redirErr}\n<span class="string">stdout_logfile</span>=/var/log/${name}.log`;
|
|
1874
|
+
}
|
|
1875
|
+
supInputs.forEach(input => input.addEventListener('input', updateSupOutput));
|
|
1876
|
+
supInputs.forEach(input => input.addEventListener('change', updateSupOutput));
|
|
1877
|
+
|
|
1878
|
+
async function copyCode(elementId, btn) {
|
|
1879
|
+
const text = document.getElementById(elementId).innerText;
|
|
1880
|
+
await ipcRenderer.invoke('copy-to-clipboard', text);
|
|
1881
|
+
const originalText = btn.innerText;
|
|
1882
|
+
btn.innerText = '✅ Kopyalandı!';
|
|
1883
|
+
setTimeout(() => { btn.innerText = originalText; }, 2000);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
updateCronOutput();
|
|
1887
|
+
updateSupOutput();
|
|
1888
|
+
</script>
|
|
1889
|
+
<script src="make-wizard.js"></script>
|
|
1890
|
+
<script src="api-inspector.js"></script>
|
|
1891
|
+
</body>
|
|
1892
|
+
</html>
|