@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.
Files changed (90) hide show
  1. package/LICENSE +660 -660
  2. package/bin/artiframe.js +0 -0
  3. package/bin/artiframe.php +0 -0
  4. package/core-stubs/app/ApiAuthControl.php +76 -0
  5. package/core-stubs/app/ApiControl.php +81 -35
  6. package/core-stubs/app/AuthControl.php +39 -0
  7. package/core-stubs/app/Database.php +36 -35
  8. package/core-stubs/app/DotEnv.php +19 -1
  9. package/core-stubs/app/ViewControl.php +47 -1
  10. package/core-stubs/bin/SQLMethod.php +261 -0
  11. package/core-stubs/bin/SystemMethod.php +115 -74
  12. package/core-stubs/bin/ViewMethod.php +19 -5
  13. package/core-stubs/docs/de.html +4325 -4347
  14. package/core-stubs/docs/en.html +4325 -4347
  15. package/core-stubs/docs/es.html +4325 -4347
  16. package/core-stubs/docs/fr.html +4325 -4347
  17. package/core-stubs/docs/tr.html +117 -46
  18. package/core-stubs/public/403.php +52 -0
  19. package/core-stubs/public/assets/css/components/header.css +174 -0
  20. package/core-stubs/public/assets/css/components/mobilenav.css +198 -0
  21. package/core-stubs/public/assets/css/components/sidebar.css +184 -0
  22. package/core-stubs/public/assets/css/root/app.css +975 -38
  23. package/core-stubs/public/assets/css/root/root.css +64 -0
  24. package/core-stubs/public/assets/js/components/header.js +380 -0
  25. package/core-stubs/public/assets/js/components/mobilenav.js +97 -0
  26. package/core-stubs/public/assets/js/components/theme-modal.js +74 -0
  27. package/core-stubs/public/assets/js/root/app.js +1870 -0
  28. package/core-stubs/public/includes/footer.php +4 -5
  29. package/core-stubs/public/includes/head-auth.php +65 -0
  30. package/core-stubs/public/includes/head.php +56 -17
  31. package/core-stubs/public/includes/header.php +135 -1
  32. package/core-stubs/public/includes/mobilenav.php +75 -3
  33. package/core-stubs/public/includes/sidebar.php +186 -1
  34. package/core-stubs/public/includes/theme-modal.php +1 -0
  35. package/db-studio/debug.log +3 -0
  36. package/db-studio/index.html +1755 -0
  37. package/db-studio/launcher.html +102 -0
  38. package/db-studio/launcher.js +180 -0
  39. package/db-studio/logo.svg +22 -0
  40. package/db-studio/main.js +568 -0
  41. package/db-studio/package-lock.json +1034 -0
  42. package/db-studio/package.json +16 -0
  43. package/db-studio/preload.js +29 -0
  44. package/devops-studio/api-inspector.js +222 -0
  45. package/devops-studio/index.html +1892 -0
  46. package/devops-studio/logo.svg +22 -0
  47. package/devops-studio/main.js +67 -0
  48. package/devops-studio/make-wizard.js +635 -0
  49. package/devops-studio/package-lock.json +871 -0
  50. package/devops-studio/package.json +12 -0
  51. package/package.json +5 -2
  52. package/scripts/postinstall.js +44 -8
  53. package/server-studio/.json-404-guard +1 -0
  54. package/server-studio/index.html +283 -0
  55. package/server-studio/logo.svg +22 -0
  56. package/server-studio/main.js +133 -0
  57. package/server-studio/package-lock.json +1179 -0
  58. package/server-studio/package.json +15 -0
  59. package/server-studio/router.php +99 -0
  60. package/src/App.php +29 -0
  61. package/src/Commands/AddCommand.php +26 -9
  62. package/src/Commands/DbExportCommand.php +131 -0
  63. package/src/Commands/DbStartCommand.php +46 -0
  64. package/src/Commands/DevopsCommand.php +35 -0
  65. package/src/Commands/GoCommand.php +48 -48
  66. package/src/Commands/ListCommand.php +68 -68
  67. package/src/Commands/MakeApiCommand.php +6 -1
  68. package/src/Commands/MakeViewCommand.php +25 -2
  69. package/src/Commands/NewProjectCommand.php +16 -11
  70. package/src/Commands/OptimizeCommand.php +61 -0
  71. package/src/Commands/RemoveCommand.php +36 -7
  72. package/src/Commands/ServeCommand.php +46 -91
  73. package/src/Commands/ShowCommand.php +24 -24
  74. package/src/Commands/UpgradeCommand.php +39 -10
  75. package/stubs/make/api-switch-case.stub +1 -1
  76. package/stubs/make/auth-api-standart.stub +20 -0
  77. package/stubs/make/auth-api-switch-case.stub +38 -0
  78. package/stubs/make/auth-view.stub +26 -0
  79. package/stubs/make/class.stub +10 -2
  80. package/stubs/make/view.stub +3 -0
  81. package/stubs/service/image.stub +334 -334
  82. package/stubs/service/iyzico.stub +637 -637
  83. package/stubs/service/jwt.stub +312 -312
  84. package/stubs/service/phpmailer.stub +143 -143
  85. package/stubs/service/pusher.stub +232 -232
  86. package/stubs/service/qrcode.stub +169 -169
  87. package/stubs/service/redis.stub +361 -361
  88. package/stubs/service/s3.stub +377 -377
  89. package/stubs/service/stripe.stub +526 -526
  90. package/stubs/service/twilio.stub +361 -361
@@ -0,0 +1,635 @@
1
+ // make-wizard.js — ArtiFrame DevOps Studio Make Wizard Logic
2
+ // NOT: fs, path, currentCwd, updateExplorerPreview, loadTree
3
+ // ana index.html script bloğunda tanımlıdır; buradan window üzerinden erişilir.
4
+
5
+ 'use strict';
6
+
7
+ let wizardActivePath = '';
8
+ let wizardActiveType = '';
9
+
10
+ function renderMakeWizard(relPath) {
11
+ wizardActivePath = relPath;
12
+ document.getElementById('make-wizard-path').innerText = 'Seçili Dizin: ' + relPath + '/';
13
+ document.getElementById('make-btn-generate').style.display = 'block';
14
+
15
+ const form = document.getElementById('make-wizard-form');
16
+ let html = '';
17
+
18
+ if (relPath.startsWith('public/api')) {
19
+ wizardActiveType = 'api';
20
+ html = '<div style="margin-bottom:15px;">';
21
+ html += '<label style="display:block;font-size:12px;color:#8b949e;margin-bottom:5px;">API Adı (Örn: auth/login veya dosya)</label>';
22
+ html += '<input type="text" id="mw-name" class="form-control" oninput="updateMakePreview()" placeholder="api_adi">';
23
+ html += '</div>';
24
+ html += '<div style="margin-bottom:15px;display:flex;gap:15px;">';
25
+ html += '<label style="display:flex;align-items:center;gap:5px;font-size:13px;color:#c9d1d9;"><input type="radio" name="mw-api-type" value="standart" onchange="updateMakePreview()" checked> Standart API</label>';
26
+ html += '<label style="display:flex;align-items:center;gap:5px;font-size:13px;color:#c9d1d9;"><input type="radio" name="mw-api-type" value="switch-case" checked onchange="updateMakePreview()"> Switch-Case API</label>';
27
+ html += '</div>';
28
+ html += '<div><label style="display:flex;align-items:center;gap:5px;font-size:13px;color:#c9d1d9;"><input type="checkbox" id="mw-api-guest" onchange="updateMakePreview()"> Misafirlere Açık Mı? (ApiAuthControl)</label></div>';
29
+
30
+ } else if (relPath.startsWith('src') || relPath.startsWith('app') || relPath.startsWith('config')) {
31
+ wizardActiveType = 'class';
32
+ html = '<div style="margin-bottom:15px;">';
33
+ html += '<label style="display:block;font-size:12px;color:#8b949e;margin-bottom:5px;">Sınıf Adı (Örn: User, Payment/Stripe)</label>';
34
+ html += '<input type="text" id="mw-name" class="form-control" oninput="updateMakePreview()" placeholder="SinifAdi">';
35
+ html += '</div>';
36
+ html += '<div style="margin-bottom:15px;"><label style="display:flex;align-items:center;gap:5px;font-size:13px;color:#c9d1d9;"><input type="checkbox" id="mw-class-singleton" onchange="updateMakePreview()"> Singleton (getInstance) kalıbı ekle</label></div>';
37
+ html += '<div><label style="display:flex;align-items:center;gap:5px;font-size:13px;color:#c9d1d9;"><input type="checkbox" id="mw-class-crud" onchange="updateMakePreview()"> Standart CRUD (create, update, delete vb.) iskeleti ekle</label></div>';
38
+
39
+ } else if (relPath.startsWith('public')) {
40
+ wizardActiveType = 'view';
41
+ html = '<div style="margin-bottom:15px;">';
42
+ html += '<label style="display:block;font-size:12px;color:#8b949e;margin-bottom:5px;">Sayfa Adı (Örn: panel/ayarlar)</label>';
43
+ html += '<input type="text" id="mw-name" class="form-control" oninput="updateMakePreview()" placeholder="sayfa-adi">';
44
+ html += '</div>';
45
+ html += '<div style="margin-bottom:15px;"><label style="display:flex;align-items:center;gap:5px;font-size:13px;color:#c9d1d9;"><input type="checkbox" id="mw-view-layout" checked onchange="updateMakePreview()"> Standart App Layout (Header/Sidebar) eklensin</label></div>';
46
+ html += '<div><label style="display:flex;align-items:center;gap:5px;font-size:13px;color:#c9d1d9;"><input type="checkbox" id="mw-view-assets" checked onchange="updateMakePreview()"> Özel CSS/JS dosyası oluşturulup sayfaya bağlansın</label></div>';
47
+
48
+ } else {
49
+ wizardActiveType = null;
50
+ document.getElementById('make-btn-generate').style.display = 'none';
51
+ html = '<div style="color:#8b949e;font-size:13px;text-align:center;">Bu klasör için üretim şablonu yok. Sağ tıklayarak dosya ekleyebilirsiniz.</div>';
52
+ }
53
+
54
+ form.innerHTML = html;
55
+ updateMakePreview();
56
+ }
57
+
58
+ function updateMakePreview() {
59
+ const preview = document.getElementById('make-wizard-preview');
60
+ const pathInfo = document.getElementById('make-wizard-path');
61
+
62
+ if (!wizardActiveType) {
63
+ preview.innerText = '';
64
+ if(pathInfo) pathInfo.innerText = 'Klasör seçimi bekleniyor...';
65
+ return;
66
+ }
67
+
68
+ const nameEl = document.getElementById('mw-name');
69
+ const name = nameEl ? nameEl.value.trim() : '';
70
+
71
+ if (pathInfo) {
72
+ let finalPath = wizardActivePath;
73
+ if (wizardActiveType === 'api') {
74
+ const apiTypeEl = document.querySelector('input[name="mw-api-type"]:checked');
75
+ if (apiTypeEl && (wizardActivePath === 'public/api' || wizardActivePath === 'public/api/')) {
76
+ finalPath = wizardActivePath + '/' + apiTypeEl.value;
77
+ }
78
+ }
79
+ let ext = name ? (name.endsWith('.php') ? '' : '.php') : '';
80
+ pathInfo.innerText = 'Hedef: ' + finalPath + '/' + name + ext;
81
+ }
82
+
83
+ if (!name) { preview.innerText = '// Lütfen bir isim girin...'; return; }
84
+
85
+ const L = [];
86
+
87
+ if (wizardActiveType === 'api') {
88
+ const isGuest = document.getElementById('mw-api-guest').checked;
89
+ const apiType = document.querySelector('input[name="mw-api-type"]:checked').value;
90
+ const bootstrap = isGuest ? 'ApiAuthControl' : 'ApiControl';
91
+
92
+ L.push('<?php');
93
+ L.push("$allowedMethods = ['POST'];");
94
+ L.push("require_once $_SERVER['DOCUMENT_ROOT'] . '/../app/" + bootstrap + ".php';");
95
+ L.push('', 'use Bin\\SystemMethod;', '');
96
+
97
+ if (isGuest) {
98
+ L.push("// Misafir API: CSRF manuel dogrulanmalidir.");
99
+ L.push("if (!SystemMethod::verifyCsrf($_POST['csrf_token'] ?? '')) {");
100
+ L.push(" jsonResponse(['status' => 'error', 'message' => 'Gecersiz CSRF token.'], 403);");
101
+ L.push('}');
102
+ } else {
103
+ L.push('// ApiControl: Auth ve CSRF otomatik dogrulandi.');
104
+ }
105
+ L.push('');
106
+
107
+ if (apiType === 'switch-case') {
108
+ L.push("$action = SystemMethod::sanitizeString($_POST['action'] ?? '');", '');
109
+ L.push('switch ($action) {');
110
+ L.push(" case 'ornek_islem':", ' // Islemler...', " jsonResponse(['status' => 'success'], 200);", ' break;');
111
+ L.push('', ' default:', " jsonResponse(['status' => 'error', 'message' => 'Gecersiz islem.'], 400);", '}');
112
+ } else {
113
+ L.push('// Islemler...', "jsonResponse(['status' => 'success'], 200);");
114
+ }
115
+
116
+ } else if (wizardActiveType === 'class') {
117
+ const isSingleton = document.getElementById('mw-class-singleton').checked;
118
+ const isCrud = document.getElementById('mw-class-crud').checked;
119
+
120
+ const raw = (wizardActivePath + '/' + name).split('/').filter(Boolean);
121
+ const clsName = raw[raw.length - 1].charAt(0).toUpperCase() + raw[raw.length - 1].slice(1);
122
+ const ns = raw.slice(0, -1).map(function(p) { return p.charAt(0).toUpperCase() + p.slice(1); }).join('\\');
123
+
124
+ L.push('<?php', 'namespace ' + ns + ';', '', 'use App\\Database;', 'use Bin\\SystemMethod;', '', 'class ' + clsName, '{');
125
+
126
+ if (isSingleton) {
127
+ L.push(
128
+ ' private static ?' + clsName + ' $instance = null;',
129
+ ' public $db;', '',
130
+ ' private function __construct()',
131
+ ' {',
132
+ ' $this->db = \App\Database::getInstance();',
133
+ ' }', '',
134
+ ' public static function getInstance(): ' + clsName,
135
+ ' {',
136
+ ' if (self::$instance === null) { self::$instance = new self(); }',
137
+ ' return self::$instance;',
138
+ ' }'
139
+ );
140
+ }
141
+
142
+ if (isCrud) {
143
+ var m = isSingleton ? '' : 'static ';
144
+ var methods = [
145
+ ['create', 'array $data', 'array', ["$id = SystemMethod::byteId();", "$result = dbInsert('tablo_adi', $data);", "return ['success' => (bool)$result, 'id' => bin2hex($id)];"]],
146
+ ['update', 'string $id, array $data', 'bool', ["$result = dbUpdate('tablo_adi', $data, ['id' => $id]);", "return (bool)$result;"]],
147
+ ['delete', 'string $id', 'bool', ["$result = dbDelete('tablo_adi', ['id' => $id]);", "return (bool)$result;"]],
148
+ ['getById', 'string $id', '?array', ["return dbGet('tablo_adi', ['id' => $id]);"]],
149
+ ['getList', 'array $filters = [], int $limit = 100', 'array', ["return dbList('tablo_adi', $filters, '', $limit);"]],
150
+ ];
151
+ methods.forEach(function(def) {
152
+ var fn = def[0], params = def[1], ret = def[2], body = def[3];
153
+ L.push('', ' public ' + m + 'function ' + fn + '(' + params + '): ' + ret, ' {');
154
+ body.forEach(function(l) { L.push(' ' + l); });
155
+ L.push(' }');
156
+ });
157
+ }
158
+
159
+ if (!isSingleton && !isCrud) {
160
+ L.push('', ' public static function doSomething()', ' {', ' // ...', ' }');
161
+ }
162
+ L.push('}');
163
+
164
+ } else if (wizardActiveType === 'view') {
165
+ var isLayout = document.getElementById('mw-view-layout').checked;
166
+ var isAssets = document.getElementById('mw-view-assets').checked;
167
+ var baseName = name.split('/').pop();
168
+ var titleName = baseName.charAt(0).toUpperCase() + baseName.slice(1);
169
+
170
+ L.push('<?php', "require_once $_SERVER['DOCUMENT_ROOT'] . '/../app/ViewControl.php';", 'use Bin\\ViewMethod;', '', '?>');
171
+ L.push('<!DOCTYPE html>', '<html lang="tr" data-theme="default" data-mode="light">', '<head>');
172
+ var headFile = (wizardActivePath.includes('public/auth')) ? 'head-auth.php' : 'head.php';
173
+ L.push(" <?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/' + headFile + ''; ?>");
174
+ L.push(' <title>' + titleName + '</title>');
175
+ if (isAssets) L.push(' <link rel="stylesheet" href="/assets/css/' + name + '.css">');
176
+ L.push('</head>', '<body>');
177
+
178
+ if (isLayout) {
179
+ L.push(
180
+ ' <div class="app-layout">',
181
+ " <?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/sidebar.php'; ?>",
182
+ ' <div class="app-main-content">',
183
+ " <?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; ?>",
184
+ ' <main class="main-content">',
185
+ ' <div class="card">',
186
+ ' <h3>' + baseName + '</h3>',
187
+ ' <p>Icerik buraya...</p>',
188
+ ' </div>',
189
+ ' </main>',
190
+ ' </div>',
191
+ ' </div>'
192
+ );
193
+ } else {
194
+ L.push(' <main>', ' <h1>' + baseName + '</h1>', ' </main>');
195
+ }
196
+ if (isAssets) L.push(' <script src="/assets/js/' + name + '.js"><\/script>');
197
+ L.push('</body>', '</html>');
198
+ }
199
+
200
+ preview.innerText = L.join('\n');
201
+ }
202
+
203
+ function executeMakeWizard() {
204
+ if (!wizardActiveType || !wizardActivePath) return;
205
+ var nameEl = document.getElementById('mw-name');
206
+ var name = nameEl ? nameEl.value.trim() : '';
207
+ if (!name) { alert('Lutfen isim girin.'); return; }
208
+
209
+ var safeName = name;
210
+ if (wizardActiveType === 'class') {
211
+ var parts = name.split('/');
212
+ parts[parts.length - 1] = parts[parts.length - 1].charAt(0).toUpperCase() + parts[parts.length - 1].slice(1);
213
+ safeName = parts.join('/');
214
+ }
215
+ var ext = safeName.endsWith('.php') ? '' : '.php';
216
+
217
+ // API için standart veya switch-case seçimine göre alt klasöre yönlendir
218
+ var finalPath = wizardActivePath;
219
+ if (wizardActiveType === 'api') {
220
+ var apiType = document.querySelector('input[name="mw-api-type"]:checked').value;
221
+ if (wizardActivePath === 'public/api' || wizardActivePath === 'public/api/') {
222
+ finalPath = path.join(wizardActivePath, apiType);
223
+ }
224
+ }
225
+
226
+ var targetPath = path.join(currentCwd, finalPath, safeName + ext);
227
+
228
+ if (fs.existsSync(targetPath)) { alert('Hata: Dosya zaten var!\n' + targetPath); return; }
229
+
230
+ try {
231
+ var dir = path.dirname(targetPath);
232
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
233
+
234
+ var code = document.getElementById('make-wizard-preview').innerText;
235
+ fs.writeFileSync(targetPath, code, 'utf8');
236
+ var log = '✅ PHP Dosyasi olusturuldu:\n' + targetPath + '\n';
237
+
238
+ if (wizardActiveType === 'view') {
239
+ var isAssets2 = document.getElementById('mw-view-assets').checked;
240
+ if (isAssets2) {
241
+ var cssPath = path.join(currentCwd, 'public', 'assets', 'css', name + '.css');
242
+ var jsPath = path.join(currentCwd, 'public', 'assets', 'js', name + '.js');
243
+ [cssPath, jsPath].forEach(function(p) {
244
+ var d = path.dirname(p);
245
+ if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
246
+ });
247
+ if (!fs.existsSync(cssPath)) { fs.writeFileSync(cssPath, '/* ' + name + ' */'); log += '✅ CSS olusturuldu:\n' + cssPath + '\n'; }
248
+ if (!fs.existsSync(jsPath)) { fs.writeFileSync(jsPath, '// ' + name + '.js\n\n// CSRF Token (API istekleri icin zorunludur)\nconst csrfToken = document.querySelector(\'meta[name="csrf-token"]\')?.getAttribute(\'content\');\n'); log += '✅ JS olusturuldu:\n' + jsPath + '\n'; }
249
+ }
250
+ }
251
+
252
+ updateExplorerPreview(log);
253
+ loadTree();
254
+ } catch (e) {
255
+ alert('Hata olustu: ' + e.message);
256
+ }
257
+ }
258
+ // handleFileClick hook for DevOps Studio
259
+ function handleFileClick(relPath) {
260
+ if (!relPath.endsWith('.php') && !relPath.endsWith('.css') && !relPath.endsWith('.js')) {
261
+ document.getElementById('exp-tab-cheat').innerHTML = '<div style="padding:30px; text-align:center; color:#8b949e;">Sadece kod dosyaları analiz edilebilir.</div>';
262
+ document.getElementById('exp-tab-arch').innerHTML = '<div style="padding:30px; text-align:center; color:#8b949e;">Sadece kod dosyaları analiz edilebilir.</div>';
263
+ if (typeof switchExpTab === 'function') switchExpTab('cheat');
264
+ return;
265
+ }
266
+
267
+ const fullPath = path.join(currentCwd, relPath);
268
+ let content = '';
269
+ try {
270
+ content = fs.readFileSync(fullPath, 'utf8');
271
+ } catch (e) {
272
+ return;
273
+ }
274
+
275
+ // ==========================================
276
+ // TAB 3: MİMARİ BAĞIMLILIK HARİTASI (RÖNTGEN)
277
+ // ==========================================
278
+ let roleTitle = "Bilinmeyen Dosya";
279
+ let roleDesc = "Bu dosyanın projedeki temel amacı anlaşılamadı.";
280
+ let roleIcon = "❓";
281
+ let isView = false;
282
+ let isClass = false;
283
+ let isApi = false;
284
+
285
+ if (relPath.startsWith('public/api')) {
286
+ isApi = true;
287
+ roleTitle = "API Uç Noktası (Endpoint)";
288
+ roleDesc = "Bu dosya, mobil uygulamalar veya dış sistemlerle JSON formatında haberleşmeyi sağlar. Arayüzü yoktur, sadece veri alıp veri gönderir.";
289
+ roleIcon = "⚡";
290
+ } else if (relPath.startsWith('public/')) {
291
+ isView = true;
292
+ roleTitle = "Kullanıcı Arayüzü (View)";
293
+ roleDesc = "Kullanıcıların tarayıcıda gördüğü HTML tasarımını ve sayfayı barındırır. Son kullanıcının etkileşime girdiği yerdir.";
294
+ roleIcon = "🖥️";
295
+ } else if (relPath.startsWith('app/')) {
296
+ isClass = true;
297
+ roleTitle = "Sistem Çekirdeği (Core)";
298
+ roleDesc = "Framework'ün kalbidir. Veritabanı bağlantısı, güvenlik kalkanları veya temel ayarlar burada barınır. (Dikkatli düzenlenmelidir).";
299
+ roleIcon = "⚙️";
300
+ } else if (relPath.startsWith('src/')) {
301
+ isClass = true;
302
+ roleTitle = "İş Mantığı (Service / Model)";
303
+ roleDesc = "Uygulamanın beynidir. Karmaşık hesaplamalar, veritabanı sorguları ve kurallar burada yazılır. View veya API'ler buradaki kodları çağırır.";
304
+ roleIcon = "🧠";
305
+ }
306
+
307
+ const usesDB = /dbList|dbGet|dbInsert|dbUpdate|dbDelete|\bPDO\b|\$this->db->prepare/i.test(content);
308
+ const usesViewClass = /ViewMethod::/i.test(content);
309
+
310
+ // Auth Mantığı
311
+ let authTitle = "Korunmasız Dosya";
312
+ let authDesc = "Bu dosyada hiçbir kontrol sınıfı çağırılmamış! Sistemin güvenlik duvarlarından (ViewControl/ApiControl) geçmiyor. Tehlikeli olabilir.";
313
+ let authColor = "#ff7b72"; // Red
314
+
315
+ // Açık sistem dosyaları ve hata sayfaları istisnası
316
+ const publicFiles = ['404.php', '503.php', '403.php'];
317
+ let isPublicSystemFile = publicFiles.some(f => relPath.endsWith(f));
318
+ let isRouter = relPath.endsWith('index.php');
319
+
320
+ if (!relPath.startsWith('public/')) {
321
+ authTitle = "Kapalı Kutu (Backend)";
322
+ authDesc = "Bu dosya web dizinine (public) açık DEĞİLDİR! Tarayıcıdan URL yazılarak doğrudan erişilemez. Sadece sistem içinden güvenle çağrılabilir.";
323
+ authColor = "#3fb950"; // Green
324
+ } else {
325
+ if (/AuthControl|AuthApiControl/i.test(content)) {
326
+ authTitle = "Ziyaretçi Kalkanı (Misafir)";
327
+ authDesc = "Bu dosya aslında herkese (misafirlere) AÇIKTIR. Güvenlik/oturum katmanı yoktur. Aksine, hali hazırda giriş yapmış kullanıcılar buraya giremez (Örn: Giriş sayfası).";
328
+ authColor = "#d2a8ff"; // Purple
329
+ } else if (/ViewControl|ApiControl/i.test(content)) {
330
+ authTitle = "Sistem Güvenlik Katmanı (Korumalı)";
331
+ authDesc = "Uygulamanın asıl kalkanları olan ViewControl / ApiControl tarafından korunmaktadır. RBAC ve oturum (Session) denetimleri burada işler.";
332
+ authColor = "#3fb950"; // Green
333
+ } else if (isRouter) {
334
+ authTitle = "Ana Yönlendirici (Router)";
335
+ authDesc = "Bu dosya tüm istekleri karşılayıp ilgili sayfalara dağıtır. Doğası gereği herkese açıktır. Güvenlik, yönlendirdiği alt dosyaların sorumluluğundadır.";
336
+ authColor = "#58a6ff"; // Blue
337
+ } else if (isPublicSystemFile) {
338
+ authTitle = "Sistem Hata Sayfası";
339
+ authDesc = "Bu bir hata veya durum sayfasıdır. Güvenlik bariyeri uygulanmaz, doğası gereği herkese açık olmak zorundadır.";
340
+ authColor = "#58a6ff"; // Blue
341
+ }
342
+ }
343
+
344
+ // PHP Bağımlılıkları (use)
345
+ const uses = [];
346
+ const useRegex = /use\s+([^;]+);/g;
347
+ let uMatch;
348
+ while ((uMatch = useRegex.exec(content)) !== null) {
349
+ uses.push(uMatch[1].trim());
350
+ }
351
+
352
+ // View için CSS/JS ve Include taraması
353
+ const assets = [];
354
+ const cssRegex = /href="([^"]+\.css[^"]*)"/g;
355
+ const jsRegex = /src="([^"]+\.js[^"]*)"/g;
356
+ let aMatch;
357
+ while ((aMatch = cssRegex.exec(content)) !== null) assets.push({type: 'CSS', path: aMatch[1]});
358
+ while ((aMatch = jsRegex.exec(content)) !== null) assets.push({type: 'JS', path: aMatch[1]});
359
+
360
+ const includes = [];
361
+ const reqRegex = /(?:require_once|include_once|require|include)[^\n]*?['"]([^'"]+\.php)['"]/g;
362
+ let reqMatch;
363
+ while ((reqMatch = reqRegex.exec(content)) !== null) {
364
+ let incPath = reqMatch[1].split('/').pop();
365
+ includes.push(incPath);
366
+ }
367
+
368
+ let archHtml = '<div style="padding:30px; display:flex; flex-direction:column; gap:20px;">';
369
+
370
+ archHtml += '<div style="border-bottom:1px solid #30363d; padding-bottom:15px;">';
371
+ archHtml += '<h2 style="margin:0; color:#58a6ff; font-size:22px; display:flex; align-items:center; gap:10px;">🕸️ Mimari Röntgeni</h2>';
372
+ archHtml += '<p style="color:#8b949e; font-size:13px; margin:5px 0 0 0; line-height:1.5;">Bu panel, dosyanın uygulamanın geri kalanıyla nasıl konuştuğunu gösterir.</p>';
373
+ archHtml += '</div>';
374
+
375
+ archHtml += '<div style="display:grid; grid-template-columns:1fr 1fr; gap:15px;">';
376
+
377
+ // Rol Kartı
378
+ archHtml += '<div style="background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:15px; display:flex; flex-direction:column; gap:8px;">';
379
+ archHtml += '<div style="display:flex; align-items:center; gap:8px; font-size:15px; font-weight:bold; color:#f0f6fc;"><span>' + roleIcon + '</span> Dosyanın Görevi</div>';
380
+ archHtml += '<div style="color:#79c0ff; font-size:13px; font-weight:600;">' + roleTitle + '</div>';
381
+ archHtml += '<div style="color:#8b949e; font-size:12px; line-height:1.5;">' + roleDesc + '</div>';
382
+ archHtml += '</div>';
383
+
384
+ // Veritabanı Kartı
385
+ archHtml += '<div style="background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:15px; display:flex; flex-direction:column; gap:8px;">';
386
+ archHtml += '<div style="display:flex; align-items:center; gap:8px; font-size:15px; font-weight:bold; color:#f0f6fc;"><span>🗄️</span> Veritabanı Etkileşimi</div>';
387
+ if (usesDB) {
388
+ archHtml += '<div style="color:#3fb950; font-size:13px; font-weight:600;">Aktif Bağlantı Var</div>';
389
+ archHtml += '<div style="color:#8b949e; font-size:12px; line-height:1.5;">Bu dosya veritabanına sorgu atarak veri okuma veya yazma işlemleri yapıyor.</div>';
390
+ } else {
391
+ archHtml += '<div style="color:#8b949e; font-size:13px; font-weight:600;">Bağlantı Yok</div>';
392
+ archHtml += '<div style="color:#8b949e; font-size:12px; line-height:1.5;">Bu dosya veritabanına dokunmuyor, sadece kendi içindeki mantıksal işlemleri yapıyor.</div>';
393
+ }
394
+ archHtml += '</div>';
395
+
396
+ // Güvenlik Kartı
397
+ archHtml += '<div style="background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:15px; display:flex; flex-direction:column; gap:8px;">';
398
+ archHtml += '<div style="display:flex; align-items:center; gap:8px; font-size:15px; font-weight:bold; color:#f0f6fc;"><span>🛡️</span> Güvenlik / Erişim</div>';
399
+ archHtml += '<div style="color:' + authColor + '; font-size:13px; font-weight:600;">' + authTitle + '</div>';
400
+ archHtml += '<div style="color:#8b949e; font-size:12px; line-height:1.5;">' + authDesc + '</div>';
401
+ archHtml += '</div>';
402
+
403
+ // Tasarım Kartı
404
+ archHtml += '<div style="background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:15px; display:flex; flex-direction:column; gap:8px;">';
405
+ archHtml += '<div style="display:flex; align-items:center; gap:8px; font-size:15px; font-weight:bold; color:#f0f6fc;"><span>🎨</span> Tasarım / View Çıktısı</div>';
406
+ if (usesViewClass || isView) {
407
+ archHtml += '<div style="color:#d2a8ff; font-size:13px; font-weight:600;">Görsel Çıktı Üretiyor</div>';
408
+ archHtml += '<div style="color:#8b949e; font-size:12px; line-height:1.5;">Bu dosya/sınıf ekrana HTML tasarımı (View) basıyor.</div>';
409
+ } else {
410
+ archHtml += '<div style="color:#8b949e; font-size:13px; font-weight:600;">Görsel Çıktı Yok</div>';
411
+ archHtml += '<div style="color:#8b949e; font-size:12px; line-height:1.5;">Arka planda çalışır. Ekrana tasarım basmaz.</div>';
412
+ }
413
+ archHtml += '</div>';
414
+
415
+ // View ise Varlıklar
416
+ if (isView) {
417
+ archHtml += '<div style="background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:15px; display:flex; flex-direction:column; gap:8px; grid-column:1/-1;">';
418
+ archHtml += '<div style="display:flex; align-items:center; gap:8px; font-size:15px; font-weight:bold; color:#f0f6fc;"><span>🔗</span> View Bağımlılıkları (Assets & Includes)</div>';
419
+
420
+ if (assets.length > 0) {
421
+ archHtml += '<div style="display:flex; flex-wrap:wrap; gap:8px; margin-top:5px;">';
422
+ assets.forEach(a => {
423
+ let badgeColor = a.type === 'CSS' ? '#d2a8ff' : '#ff7b72';
424
+ archHtml += '<span style="background:#161b22; border:1px solid #30363d; color:#c9d1d9; font-size:11px; padding:3px 8px; border-radius:4px;"><strong style="color:'+badgeColor+';">' + a.type + ':</strong> ' + a.path + '</span>';
425
+ });
426
+ archHtml += '</div>';
427
+ } else {
428
+ archHtml += '<div style="color:#8b949e; font-size:12px;">Özel CSS/JS bağımlılığı tespit edilmedi.</div>';
429
+ }
430
+
431
+ if (includes.length > 0) {
432
+ archHtml += '<div style="color:#79c0ff; font-size:12px; margin-top:10px; font-weight:600;">Dahil Edilen Modüller:</div>';
433
+ archHtml += '<div style="display:flex; flex-wrap:wrap; gap:8px;">';
434
+ includes.forEach(inc => {
435
+ archHtml += '<span style="background:#21262d; border:1px solid #444c56; color:#8b949e; font-size:11px; padding:3px 8px; border-radius:4px;">' + inc + '</span>';
436
+ });
437
+ archHtml += '</div>';
438
+ }
439
+ archHtml += '</div>';
440
+ }
441
+
442
+ archHtml += '</div>'; // End Grid
443
+
444
+ // Sınıf Bağımlılıkları (use)
445
+ if (uses.length > 0) {
446
+ archHtml += '<div style="background:#0d1117; border:1px solid #30363d; border-radius:8px; padding:15px; margin-top:15px;">';
447
+ archHtml += '<div style="font-size:14px; font-weight:bold; color:#f0f6fc; margin-bottom:10px;">📦 PHP Kütüphane Bağımlılıkları (use)</div>';
448
+ archHtml += '<div style="display:flex; flex-wrap:wrap; gap:8px;">';
449
+ uses.forEach(u => {
450
+ archHtml += '<span style="background:#161b22; border:1px solid #444c56; color:#79c0ff; font-size:12px; font-family:monospace; padding:4px 10px; border-radius:12px;">' + u + '</span>';
451
+ });
452
+ archHtml += '</div></div>';
453
+ }
454
+
455
+ archHtml += '</div>';
456
+ document.getElementById('exp-tab-arch').innerHTML = archHtml;
457
+
458
+ // ==========================================
459
+ // TAB 2: SINIF CHEAT-SHEET
460
+ // ==========================================
461
+ const namespaceMatch = content.match(/namespace\s+([^;]+);/);
462
+ const classMatch = content.match(/class\s+([a-zA-Z0-9_]+)/);
463
+
464
+ if (!classMatch) {
465
+ document.getElementById('exp-tab-cheat').innerHTML = '<div style="padding:30px; text-align:center; color:#8b949e;"><h2 style="color:#f0f6fc;">Bu bir sınıf değil.</h2><p>Bu dosyada geçerli bir Sınıf (Class) tanımı bulunamadı. Mimari Haritası sekmesinden dosya yeteneklerini inceleyebilirsiniz.</p></div>';
466
+ if (typeof window.renderApiInspector === 'function') window.renderApiInspector(relPath, content);
467
+
468
+ if (typeof switchExpTab === 'function') {
469
+ if (isApi) { switchExpTab('api'); } else if (isClass) { switchExpTab('cheat'); } else { switchExpTab('arch'); }
470
+ }
471
+ return;
472
+ }
473
+
474
+ const nsName = namespaceMatch ? namespaceMatch[1].trim() : 'Global';
475
+ const className = classMatch[1].trim();
476
+
477
+ const props = [];
478
+ const propRegex = /(public|protected|private)\s+(?:static\s+)?(?:\?[a-zA-Z0-9_\\]+\s+)?(\$[a-zA-Z0-9_]+)/g;
479
+ let pMatch;
480
+ while ((pMatch = propRegex.exec(content)) !== null) {
481
+ props.push({ visibility: pMatch[1], name: pMatch[2] });
482
+ }
483
+
484
+ const methods = [];
485
+ const methodRegex = /(?:(\/\*\*(?:(?!\*\/)[\s\S])*?\*\/)\s*)?(public|protected|private)\s+(static\s+)?function\s+([a-zA-Z0-9_]+)\s*\(([\s\S]*?)\)(?:\s*:\s*([a-zA-Z0-9_?\\|]+))?/g;
486
+ let mMatch;
487
+ while ((mMatch = methodRegex.exec(content)) !== null) {
488
+ let rawDoc = mMatch[1] || '';
489
+ let cleanDoc = rawDoc.replace(/\/\*\*|\*\/|^\s*\*\s?/gm, '').trim();
490
+
491
+ methods.push({
492
+ visibility: mMatch[2],
493
+ isStatic: !!mMatch[3],
494
+ name: mMatch[4],
495
+ params: mMatch[5].trim(),
496
+ returnType: mMatch[6] ? mMatch[6].trim() : 'mixed',
497
+ doc: cleanDoc,
498
+ id: 'method-' + mMatch[4]
499
+ });
500
+ }
501
+
502
+ let cheatHtml = '<div style="padding:30px; display:flex; flex-direction:column; gap:20px;">';
503
+ cheatHtml += '<div style="border-bottom:1px solid #30363d; padding-bottom:15px;">';
504
+ cheatHtml += '<h2 style="margin:0; color:#58a6ff; font-size:24px; display:flex; align-items:center; gap:10px;">';
505
+ cheatHtml += '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>';
506
+ cheatHtml += className + '</h2>';
507
+ cheatHtml += '<div style="color:#8b949e; font-size:13px; margin-top:5px; font-family:monospace;">namespace ' + nsName + ';</div>';
508
+ cheatHtml += '</div>';
509
+
510
+ if (props.length > 0) {
511
+ cheatHtml += '<div>';
512
+ cheatHtml += '<h4 style="color:#f0f6fc; border-bottom:1px solid #30363d; padding-bottom:8px; margin-bottom:10px;">Özellikler</h4>';
513
+ cheatHtml += '<div style="display:flex; flex-wrap:wrap; gap:6px;">';
514
+ props.forEach(p => {
515
+ const color = p.visibility === 'public' ? '#3fb950' : (p.visibility === 'protected' ? '#d2a8ff' : '#ff7b72');
516
+ cheatHtml += '<div style="display:flex; align-items:center; gap:8px; font-family:monospace; font-size:12px; background:#0d1117; padding:4px 10px; border-radius:4px; border:1px solid #30363d;">';
517
+ cheatHtml += '<span style="color:' + color + ';">' + p.visibility + '</span>';
518
+ cheatHtml += '<span style="color:#c9d1d9;">' + p.name + '</span>';
519
+ cheatHtml += '</div>';
520
+ });
521
+ cheatHtml += '</div></div>';
522
+ }
523
+
524
+ if (methods.length > 0) {
525
+ cheatHtml += '<div>';
526
+ cheatHtml += '<div style="display:flex; justify-content:space-between; align-items:flex-end; border-bottom:1px solid #30363d; padding-bottom:8px; margin-bottom:10px;">';
527
+ cheatHtml += '<h4 style="color:#f0f6fc; margin:0;">Metodlar</h4>';
528
+ cheatHtml += '<input type="text" id="mw-method-search" placeholder="Metod Ara..." style="background:#0d1117; border:1px solid #30363d; color:#c9d1d9; border-radius:4px; padding:4px 8px; font-size:12px; width:150px; outline:none;" onkeyup="filterMethods()">';
529
+ cheatHtml += '</div>';
530
+
531
+ cheatHtml += '<div id="mw-methods-list" style="display:flex; flex-direction:column; gap:8px;">';
532
+ methods.forEach(m => {
533
+ const color = m.visibility === 'public' ? '#3fb950' : (m.visibility === 'protected' ? '#d2a8ff' : '#ff7b72');
534
+ const staticBadge = m.isStatic ? '<span style="background:#21262d; border:1px solid #444c56; color:#8b949e; font-size:10px; padding:2px 6px; border-radius:10px;">STATIC</span>' : '';
535
+ const usageExample = m.isStatic ? `${className}::${m.name}(${m.params ? '...' : ''})` : `$obj->${m.name}(${m.params ? '...' : ''})`;
536
+ const hasDetails = m.params || m.doc || m.returnType !== 'mixed';
537
+
538
+ cheatHtml += '<div class="mw-method-item" data-name="' + m.name.toLowerCase() + '" style="background:#0d1117; padding:12px; border-radius:6px; border:1px solid #30363d; display:flex; flex-direction:column; gap:8px;">';
539
+
540
+ cheatHtml += '<div style="display:flex; justify-content:space-between; align-items:center;">';
541
+ cheatHtml += '<div style="display:flex; align-items:center; gap:8px; font-family:monospace; font-size:14px; flex-wrap:wrap;">';
542
+ cheatHtml += '<span style="color:' + color + ';">' + m.visibility + '</span>';
543
+ cheatHtml += staticBadge;
544
+ cheatHtml += '<span style="color:#79c0ff; font-weight:bold;">' + m.name + '</span>';
545
+ cheatHtml += '<span style="color:#8b949e;">(' + (m.params ? '...' : '') + ')</span>';
546
+ cheatHtml += '</div>';
547
+
548
+ cheatHtml += '<div style="display:flex; gap:6px;">';
549
+ if (hasDetails) {
550
+ cheatHtml += `<button onclick="toggleMethodDetails('${m.id}')" style="background:#161b22; border:1px solid #30363d; color:#58a6ff; cursor:pointer; padding:4px 8px; border-radius:4px; font-size:11px;">Detaylar</button>`;
551
+ }
552
+ cheatHtml += `<button onclick="copyToClipboard('${usageExample.replace(/'/g, "\'")}', this)" style="background:#161b22; border:1px solid #30363d; color:#8b949e; cursor:pointer; padding:4px 8px; border-radius:4px; font-size:11px; width:75px;">Kopyala</button>`;
553
+ cheatHtml += '</div>';
554
+ cheatHtml += '</div>';
555
+
556
+ if (hasDetails) {
557
+ cheatHtml += `<div id="${m.id}" style="display:none; margin-top:8px; padding-top:8px; border-top:1px dashed #30363d; flex-direction:column; gap:8px; font-size:12px;">`;
558
+
559
+ if (m.doc) {
560
+ cheatHtml += '<div style="color:#a5d6ff; font-style:italic; white-space:pre-wrap;">' + m.doc + '</div>';
561
+ }
562
+
563
+ if (m.params) {
564
+ cheatHtml += '<div style="background:#161b22; padding:8px; border-radius:4px; font-family:monospace; color:#e6edf3;">';
565
+ cheatHtml += '<div style="color:#8b949e; margin-bottom:4px;">Parametreler:</div>';
566
+ let coloredParams = m.params.replace(/(\$[a-zA-Z0-9_]+)/g, '<span style="color:#79c0ff;">$1</span>');
567
+ coloredParams = coloredParams.replace(/([a-zA-Z0-9_\\]+)\s+\$[a-zA-Z0-9_]+/g, '<span style="color:#ff7b72;">$1</span> $$');
568
+ cheatHtml += coloredParams;
569
+ cheatHtml += '</div>';
570
+ }
571
+
572
+ cheatHtml += '<div style="display:flex; gap:5px; align-items:center; color:#8b949e; font-family:monospace;">';
573
+ cheatHtml += 'Dönüş (Return): <span style="color:#d2a8ff; font-weight:bold;">' + m.returnType + '</span>';
574
+ cheatHtml += '</div>';
575
+
576
+ cheatHtml += '</div>';
577
+ }
578
+
579
+ cheatHtml += '</div>';
580
+ });
581
+ cheatHtml += '</div></div>';
582
+ }
583
+
584
+ if (props.length === 0 && methods.length === 0) {
585
+ cheatHtml += '<div style="color:#8b949e; font-size:13px; text-align:center; margin-top:20px;">Sınıfa ait özellik veya metod bulunamadı.</div>';
586
+ }
587
+
588
+ cheatHtml += '</div>';
589
+ document.getElementById('exp-tab-cheat').innerHTML = cheatHtml;
590
+
591
+ if (typeof window.renderApiInspector === 'function') window.renderApiInspector(relPath, content);
592
+
593
+ if (typeof switchExpTab === 'function') {
594
+ if (isApi) { switchExpTab('api'); } else if (isClass) { switchExpTab('cheat'); } else { switchExpTab('arch'); }
595
+ }
596
+ }
597
+
598
+ // Global Filter function for Methods
599
+ window.filterMethods = function() {
600
+ const query = document.getElementById('mw-method-search').value.toLowerCase();
601
+ const items = document.querySelectorAll('.mw-method-item');
602
+ items.forEach(item => {
603
+ const name = item.getAttribute('data-name');
604
+ if (name.includes(query)) {
605
+ item.style.display = 'flex';
606
+ } else {
607
+ item.style.display = 'none';
608
+ }
609
+ });
610
+ };
611
+
612
+ // Clipboard helper
613
+ function copyToClipboard(text, btn) {
614
+ navigator.clipboard.writeText(text).then(() => {
615
+ if (btn) {
616
+ const old = btn.innerText;
617
+ btn.innerText = 'Kopyalandı!';
618
+ btn.style.color = '#3fb950';
619
+ btn.style.borderColor = '#3fb950';
620
+ setTimeout(() => {
621
+ btn.innerText = old;
622
+ btn.style.color = '#8b949e';
623
+ btn.style.borderColor = '#30363d';
624
+ }, 1500);
625
+ }
626
+ });
627
+ }
628
+
629
+ // Global Toggle function for Method Details
630
+ window.toggleMethodDetails = function(id) {
631
+ const el = document.getElementById(id);
632
+ if (el) {
633
+ el.style.display = el.style.display === 'none' ? 'flex' : 'none';
634
+ }
635
+ };