@artilingo/artiframe-cli 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/core-stubs/bin/SystemMethod.php +600 -521
  2. package/core-stubs/bin/ViewMethod.php +591 -393
  3. package/core-stubs/docs/de.html +853 -1172
  4. package/core-stubs/docs/en.html +585 -904
  5. package/core-stubs/docs/es.html +934 -1253
  6. package/core-stubs/docs/fr.html +913 -1232
  7. package/core-stubs/docs/tr.html +4212 -5021
  8. package/package.json +1 -1
  9. package/src/App.php +38 -0
  10. package/src/Commands/AddCommand.php +1 -1
  11. package/src/Commands/AuthCommand.php +219 -0
  12. package/src/Commands/IssuesCommand.php +174 -0
  13. package/src/Commands/ListCommand.php +2 -1
  14. package/src/Commands/MakeApiCommand.php +1 -1
  15. package/src/Commands/MakeClassCommand.php +1 -1
  16. package/src/Commands/MakeViewCommand.php +1 -1
  17. package/src/Commands/NewProjectCommand.php +37 -25
  18. package/src/Commands/RemoveCommand.php +21 -10
  19. package/src/Commands/ServeCommand.php +29 -9
  20. package/src/Commands/SuggestCommand.php +143 -0
  21. package/src/Commands/TableCommand.php +2 -1
  22. package/src/Commands/UpgradeCommand.php +209 -0
  23. package/src/Commands/VersionCommand.php +2 -1
  24. package/src/Lang/de.php +9 -1
  25. package/src/Lang/en.php +8 -0
  26. package/src/Lang/es.php +9 -1
  27. package/src/Lang/fr.php +8 -0
  28. package/src/Lang/tr.php +10 -2
  29. package/src/Services/Safeguard.php +25 -0
  30. package/stubs/service/image.stub +334 -334
  31. package/stubs/service/iyzico.stub +637 -637
  32. package/stubs/service/jwt.stub +312 -312
  33. package/stubs/service/phpmailer.stub +143 -143
  34. package/stubs/service/pusher.stub +232 -232
  35. package/stubs/service/qrcode.stub +169 -169
  36. package/stubs/service/redis.stub +361 -361
  37. package/stubs/service/s3.stub +377 -377
  38. package/stubs/service/sentry.stub +76 -106
  39. package/stubs/service/stripe.stub +526 -526
  40. package/stubs/service/twilio.stub +361 -361
  41. package/core-stubs/app/R2Manager.php +0 -130
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artilingo/artiframe-cli",
3
- "version": "1.3.0",
3
+ "version": "2.0.0",
4
4
  "description": "ArtiFrame — Zero-dependency native PHP framework with a powerful multilingual CLI. Scaffold projects, generate views, APIs and classes instantly.",
5
5
  "main": "bin/artiframe.js",
6
6
  "bin": {
package/src/App.php CHANGED
@@ -199,6 +199,10 @@ class App
199
199
  case 'new':
200
200
  (new \ArtiFrame\Cli\Commands\NewProjectCommand($this->translator))->execute($commandArgs);
201
201
  break;
202
+
203
+ case 'upgrade':
204
+ (new \ArtiFrame\Cli\Commands\UpgradeCommand($this->translator))->execute($commandArgs);
205
+ break;
202
206
 
203
207
  case 'make:view':
204
208
  (new \ArtiFrame\Cli\Commands\MakeViewCommand($this->translator))->execute($commandArgs);
@@ -230,6 +234,18 @@ class App
230
234
  (new \ArtiFrame\Cli\Commands\ServeCommand($this->translator))->execute($commandArgs);
231
235
  break;
232
236
 
237
+ case 'auth':
238
+ (new \ArtiFrame\Cli\Commands\AuthCommand($this->translator))->execute($commandArgs);
239
+ break;
240
+
241
+ case 'suggest':
242
+ (new \ArtiFrame\Cli\Commands\SuggestCommand($this->translator))->execute($commandArgs);
243
+ break;
244
+
245
+ case 'issues':
246
+ (new \ArtiFrame\Cli\Commands\IssuesCommand($this->translator))->execute($commandArgs);
247
+ break;
248
+
233
249
  case 'add':
234
250
  (new \ArtiFrame\Cli\Commands\AddCommand($this->translator))->execute($commandArgs);
235
251
  break;
@@ -290,6 +306,10 @@ class App
290
306
  echo " " . $g . "new" . $r . " " . $y . "<project-path>" . $r . PHP_EOL;
291
307
  echo " " . $d . "│" . $r . " " . $t->get('HELP_NEW_DESC') . PHP_EOL;
292
308
  echo " " . $d . "└── " . $r . "Example: " . $lg . "new my-app" . $r . PHP_EOL;
309
+ // upgrade
310
+ echo " " . $g . "upgrade" . $r . PHP_EOL;
311
+ echo " " . $d . "│" . $r . " " . $t->get('HELP_UPGRADE_DESC') . PHP_EOL;
312
+ echo " " . $d . "└── " . $r . "Example: " . $lg . "upgrade" . $r . PHP_EOL;
293
313
  echo PHP_EOL;
294
314
 
295
315
  // make:view
@@ -383,6 +403,24 @@ class App
383
403
  echo " " . $d . "└── " . $r . $lg . "add list" . $r . $d . " " . $t->get('HELP_ADD_LIST') . $r . PHP_EOL;
384
404
  echo PHP_EOL;
385
405
 
406
+ // auth
407
+ echo " " . $g . "auth" . $r . PHP_EOL;
408
+ echo " " . $d . "│" . $r . " " . $t->get('HELP_AUTH_DESC') . PHP_EOL;
409
+ echo " " . $d . "└── " . $r . "Example: " . $lg . "auth" . $r . PHP_EOL;
410
+ echo PHP_EOL;
411
+
412
+ // suggest
413
+ echo " " . $g . "suggest" . $r . PHP_EOL;
414
+ echo " " . $d . "│" . $r . " " . $t->get('HELP_SUGGEST_DESC') . PHP_EOL;
415
+ echo " " . $d . "└── " . $r . "Example: " . $lg . "suggest" . $r . PHP_EOL;
416
+ echo PHP_EOL;
417
+
418
+ // issues
419
+ echo " " . $g . "issues" . $r . " " . $y . "[id]" . $r . PHP_EOL;
420
+ echo " " . $d . "│" . $r . " " . $t->get('HELP_ISSUES_DESC') . PHP_EOL;
421
+ echo " " . $d . "└── " . $r . "Example: " . $lg . "issues" . $r . " or " . $lg . "issues 4" . $r . PHP_EOL;
422
+ echo PHP_EOL;
423
+
386
424
  // help / exit
387
425
  echo " " . $d . "─────────────────────────────────────────────────────────────" . $r . PHP_EOL;
388
426
  echo " " . $g . "help" . $r . " " . $t->get('HELP_HELP_DESC') . PHP_EOL;
@@ -82,7 +82,7 @@ class AddCommand
82
82
  'sentry' => [
83
83
  'composer' => 'sentry/sentry',
84
84
  'type' => 'integrated',
85
- 'service' => 'config/sentry.php',
85
+ 'service' => 'src/Service/Sentry.php',
86
86
  'stub' => 'sentry.stub',
87
87
  'env' => [
88
88
  '# ── sentry ──────────────────────',
@@ -0,0 +1,219 @@
1
+ <?php
2
+ namespace ArtiFrame\Cli\Commands;
3
+
4
+ use ArtiFrame\Cli\Services\Translator;
5
+
6
+ class AuthCommand
7
+ {
8
+ private Translator $translator;
9
+
10
+ // Placeholder Client ID as requested
11
+ const CLIENT_ID = 'Ov23libWFmWDGtyaSEiT';
12
+
13
+ public function __construct(Translator $translator)
14
+ {
15
+ $this->translator = $translator;
16
+ }
17
+
18
+ public function execute(array $args): void
19
+ {
20
+ echo "\033[1;36mInitializing GitHub Authentication...\033[0m" . PHP_EOL;
21
+
22
+ // 1. Request Device Code
23
+ $deviceCodeData = $this->requestDeviceCode();
24
+ if (!$deviceCodeData) {
25
+ return;
26
+ }
27
+
28
+ $deviceCode = $deviceCodeData['device_code'];
29
+ $userCode = $deviceCodeData['user_code'];
30
+ $verificationUri = $deviceCodeData['verification_uri'];
31
+ $interval = (int)($deviceCodeData['interval'] ?? 5);
32
+
33
+ // 2. Display instructions
34
+ echo PHP_EOL;
35
+ echo "\033[1;33mAction Required:\033[0m" . PHP_EOL;
36
+ echo "1. Please open your browser to: \033[1;34m" . $verificationUri . "\033[0m" . PHP_EOL;
37
+ echo "2. Enter the following code to authorize: \033[1;32m" . $userCode . "\033[0m" . PHP_EOL;
38
+ echo PHP_EOL;
39
+ echo "Waiting for authorization..." . PHP_EOL;
40
+
41
+ // 3. Open browser automatically
42
+ $this->openBrowser($verificationUri);
43
+
44
+ // 4. Poll for Access Token
45
+ $accessToken = $this->pollForToken($deviceCode, $interval);
46
+
47
+ if ($accessToken) {
48
+ // 5. Fetch user profile
49
+ $userProfile = $this->fetchUserProfile($accessToken);
50
+ $username = $userProfile['login'] ?? 'Unknown User';
51
+
52
+ // 6. Save configuration
53
+ $this->saveConfig($accessToken, $username);
54
+
55
+ echo "\033[1;32m✔ Successfully authenticated as @{$username}!\033[0m" . PHP_EOL;
56
+ }
57
+ }
58
+
59
+ private function requestDeviceCode(): ?array
60
+ {
61
+ $ch = curl_init('https://github.com/login/device/code');
62
+ $postData = http_build_query([
63
+ 'client_id' => self::CLIENT_ID,
64
+ 'scope' => 'public_repo'
65
+ ]);
66
+
67
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
68
+ curl_setopt($ch, CURLOPT_POST, true);
69
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
70
+ curl_setopt($ch, CURLOPT_HTTPHEADER, [
71
+ 'Accept: application/json'
72
+ ]);
73
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
74
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
75
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
76
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
77
+ curl_setopt($ch, CURLOPT_USERAGENT, 'ArtiFrame-CLI');
78
+
79
+ $response = curl_exec($ch);
80
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
81
+
82
+ if ($httpCode !== 200 || !$response) {
83
+ echo "\033[1;31m[-] Failed to request device code from GitHub.\033[0m" . PHP_EOL;
84
+ curl_close($ch);
85
+ return null;
86
+ }
87
+ curl_close($ch);
88
+
89
+ return json_decode($response, true);
90
+ }
91
+
92
+ private function pollForToken(string $deviceCode, int $interval): ?string
93
+ {
94
+ $url = 'https://github.com/login/oauth/access_token';
95
+
96
+ while (true) {
97
+ sleep($interval);
98
+
99
+ $ch = curl_init($url);
100
+ $postData = http_build_query([
101
+ 'client_id' => self::CLIENT_ID,
102
+ 'device_code' => $deviceCode,
103
+ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code'
104
+ ]);
105
+
106
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
107
+ curl_setopt($ch, CURLOPT_POST, true);
108
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
109
+ curl_setopt($ch, CURLOPT_HTTPHEADER, [
110
+ 'Accept: application/json'
111
+ ]);
112
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
113
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
114
+ curl_setopt($ch, CURLOPT_USERAGENT, 'ArtiFrame-CLI');
115
+
116
+ $response = curl_exec($ch);
117
+ curl_close($ch);
118
+
119
+ if ($response) {
120
+ $data = json_decode($response, true);
121
+
122
+ if (isset($data['access_token'])) {
123
+ return $data['access_token'];
124
+ }
125
+
126
+ if (isset($data['error'])) {
127
+ if ($data['error'] === 'authorization_pending') {
128
+ // Keep waiting
129
+ continue;
130
+ } elseif ($data['error'] === 'slow_down') {
131
+ $interval += 5; // Slow down polling
132
+ continue;
133
+ } elseif ($data['error'] === 'expired_token') {
134
+ echo "\033[1;31m[-] The device code expired. Please run the auth command again.\033[0m" . PHP_EOL;
135
+ return null;
136
+ } elseif ($data['error'] === 'access_denied') {
137
+ echo "\033[1;31m[-] Authorization was denied.\033[0m" . PHP_EOL;
138
+ return null;
139
+ } else {
140
+ echo "\033[1;31m[-] An error occurred: " . $data['error'] . "\033[0m" . PHP_EOL;
141
+ return null;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ private function fetchUserProfile(string $token): ?array
149
+ {
150
+ $ch = curl_init('https://api.github.com/user');
151
+
152
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
153
+ curl_setopt($ch, CURLOPT_HTTPHEADER, [
154
+ 'Accept: application/vnd.github.v3+json',
155
+ 'Authorization: Bearer ' . $token,
156
+ 'User-Agent: ArtiFrame-CLI'
157
+ ]);
158
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
159
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
160
+
161
+ $response = curl_exec($ch);
162
+ curl_close($ch);
163
+
164
+ if ($response) {
165
+ return json_decode($response, true);
166
+ }
167
+
168
+ return null;
169
+ }
170
+
171
+ private function saveConfig(string $token, string $username): void
172
+ {
173
+ $home = getenv('HOME') ?: getenv('USERPROFILE');
174
+ $configDir = $home . \DIRECTORY_SEPARATOR . '.artiframe';
175
+
176
+ if (!is_dir($configDir)) {
177
+ mkdir($configDir, 0755, true);
178
+ }
179
+
180
+ $configFile = $configDir . \DIRECTORY_SEPARATOR . 'config.json';
181
+
182
+ $config = [];
183
+ if (file_exists($configFile)) {
184
+ $existing = json_decode(file_get_contents($configFile), true);
185
+ if (is_array($existing)) {
186
+ $config = $existing;
187
+ }
188
+ }
189
+
190
+ $config['github_token'] = $token;
191
+ $config['github_user'] = $username;
192
+ $config['logged_in_at'] = date('c');
193
+
194
+ file_put_contents($configFile, json_encode($config, JSON_PRETTY_PRINT));
195
+ }
196
+
197
+ private function openBrowser(string $url): void
198
+ {
199
+ $os = php_uname('s');
200
+ $command = '';
201
+
202
+ if (stripos($os, 'win') === 0) {
203
+ $command = 'start "" "' . $url . '"';
204
+ } elseif (stripos($os, 'darwin') === 0) {
205
+ $command = 'open "' . $url . '"';
206
+ } else {
207
+ $command = 'xdg-open "' . $url . '"';
208
+ }
209
+
210
+ if ($command) {
211
+ // execute command silently in background
212
+ if (stripos($os, 'win') === 0) {
213
+ pclose(popen($command, "r"));
214
+ } else {
215
+ exec($command . ' > /dev/null 2>&1 &');
216
+ }
217
+ }
218
+ }
219
+ }
@@ -0,0 +1,174 @@
1
+ <?php
2
+ namespace ArtiFrame\Cli\Commands;
3
+
4
+ use ArtiFrame\Cli\Services\Translator;
5
+
6
+ class IssuesCommand
7
+ {
8
+ private Translator $translator;
9
+ const REPO = 'utkuthecoder/artiframe-cli';
10
+
11
+ public function __construct(Translator $translator)
12
+ {
13
+ $this->translator = $translator;
14
+ }
15
+
16
+ public function execute(array $args = []): void
17
+ {
18
+ $home = getenv('HOME') ?: getenv('USERPROFILE');
19
+ $configPath = $home . \DIRECTORY_SEPARATOR . '.artiframe' . \DIRECTORY_SEPARATOR . 'config.json';
20
+
21
+ if (!file_exists($configPath)) {
22
+ echo "\033[1;31m[-] " . $this->translator->get('SUGGEST_NOT_AUTH') . "\033[0m" . PHP_EOL;
23
+ echo "Run: \033[1;32martiframe auth\033[0m" . PHP_EOL;
24
+ return;
25
+ }
26
+
27
+ $config = json_decode(file_get_contents($configPath), true);
28
+ $token = $config['github_token'] ?? null;
29
+ $username = $config['github_user'] ?? null;
30
+
31
+ if (!$token || !$username) {
32
+ echo "\033[1;31m[-] " . $this->translator->get('SUGGEST_NOT_AUTH') . "\033[0m" . PHP_EOL;
33
+ echo "Run: \033[1;32martiframe auth\033[0m" . PHP_EOL;
34
+ return;
35
+ }
36
+
37
+ $issueId = $args[0] ?? null;
38
+
39
+ if ($issueId) {
40
+ $this->showIssueDetails($token, $username, $issueId);
41
+ } else {
42
+ $this->listIssues($token, $username);
43
+ }
44
+ }
45
+
46
+ private function listIssues(string $token, string $username): void
47
+ {
48
+ echo PHP_EOL . "\033[1;36mFetching issues for @{$username}...\033[0m" . PHP_EOL;
49
+
50
+ $url = 'https://api.github.com/repos/' . self::REPO . '/issues?creator=' . urlencode($username) . '&state=all';
51
+ $response = $this->githubApiRequest($url, $token);
52
+
53
+ if (!$response) {
54
+ echo "\033[1;31m[-] Failed to fetch issues from GitHub.\033[0m" . PHP_EOL;
55
+ return;
56
+ }
57
+
58
+ $issues = json_decode($response, true);
59
+
60
+ if (isset($issues['message'])) {
61
+ echo "\033[1;31m[-] GitHub Error: " . $issues['message'] . "\033[0m" . PHP_EOL;
62
+ return;
63
+ }
64
+
65
+ if (empty($issues)) {
66
+ echo "\033[1;33mYou haven't opened any issues yet.\033[0m" . PHP_EOL;
67
+ return;
68
+ }
69
+
70
+ echo "\033[1;37m" . str_pad("ID", 6) . str_pad("STATUS", 10) . str_pad("COMMENTS", 10) . "TITLE\033[0m" . PHP_EOL;
71
+ echo str_repeat("-", 80) . PHP_EOL;
72
+
73
+ foreach ($issues as $issue) {
74
+ if (isset($issue['pull_request'])) {
75
+ continue; // Skip PRs if they show up in issue search
76
+ }
77
+
78
+ $id = "#" . $issue['number'];
79
+ $state = $issue['state']; // open or closed
80
+ $comments = $issue['comments'];
81
+ $title = strlen($issue['title']) > 50 ? substr($issue['title'], 0, 47) . '...' : $issue['title'];
82
+
83
+ $stateColor = $state === 'open' ? "\033[1;32m" : "\033[1;31m"; // Green for open, red for closed
84
+ $displayState = ucfirst($state);
85
+
86
+ echo str_pad($id, 6) .
87
+ $stateColor . str_pad($displayState, 10) . "\033[0m" .
88
+ str_pad((string)$comments, 10) .
89
+ $title . PHP_EOL;
90
+ }
91
+
92
+ echo PHP_EOL . "\033[38;5;240mTip: To view comments for a specific issue, run `artiframe issues <ID>`\033[0m" . PHP_EOL;
93
+ }
94
+
95
+ private function showIssueDetails(string $token, string $username, string $issueId): void
96
+ {
97
+ $issueId = ltrim($issueId, '#');
98
+ echo PHP_EOL . "\033[1;36mFetching details for Issue #{$issueId}...\033[0m" . PHP_EOL;
99
+
100
+ // Fetch Issue Details
101
+ $url = 'https://api.github.com/repos/' . self::REPO . '/issues/' . $issueId;
102
+ $response = $this->githubApiRequest($url, $token);
103
+
104
+ if (!$response) {
105
+ echo "\033[1;31m[-] Failed to fetch issue details.\033[0m" . PHP_EOL;
106
+ return;
107
+ }
108
+
109
+ $issue = json_decode($response, true);
110
+
111
+ if (isset($issue['message'])) {
112
+ echo "\033[1;31m[-] GitHub Error: " . $issue['message'] . "\033[0m" . PHP_EOL;
113
+ return;
114
+ }
115
+
116
+ $stateColor = $issue['state'] === 'open' ? "\033[1;32m" : "\033[1;31m";
117
+
118
+ echo PHP_EOL;
119
+ echo "\033[1;37m[" . $issue['state'] . "] " . $issue['title'] . "\033[0m" . PHP_EOL;
120
+ echo "\033[38;5;240mOpened by @" . $issue['user']['login'] . " at " . date('Y-m-d H:i', strtotime($issue['created_at'])) . "\033[0m" . PHP_EOL;
121
+ echo str_repeat("=", 80) . PHP_EOL;
122
+ echo $this->wordWrapWithIndentation($issue['body']) . PHP_EOL;
123
+ echo str_repeat("=", 80) . PHP_EOL;
124
+
125
+ if ($issue['comments'] > 0) {
126
+ echo PHP_EOL . "\033[1;36mComments ({$issue['comments']}):\033[0m" . PHP_EOL;
127
+
128
+ $commentsUrl = $issue['comments_url'];
129
+ $commentsResponse = $this->githubApiRequest($commentsUrl, $token);
130
+ $comments = json_decode($commentsResponse, true);
131
+
132
+ if (is_array($comments)) {
133
+ foreach ($comments as $comment) {
134
+ echo PHP_EOL;
135
+ echo "\033[1;34m@" . $comment['user']['login'] . "\033[0m \033[38;5;240m(" . date('Y-m-d H:i', strtotime($comment['created_at'])) . "):\033[0m" . PHP_EOL;
136
+ echo str_repeat("-", 80) . PHP_EOL;
137
+ echo $this->wordWrapWithIndentation($comment['body']) . PHP_EOL;
138
+ echo str_repeat("-", 80) . PHP_EOL;
139
+ }
140
+ }
141
+ } else {
142
+ echo PHP_EOL . "\033[38;5;240mNo comments on this issue yet.\033[0m" . PHP_EOL;
143
+ }
144
+ echo PHP_EOL;
145
+ }
146
+
147
+ private function githubApiRequest(string $url, string $token): ?string
148
+ {
149
+ $ch = curl_init($url);
150
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
151
+ curl_setopt($ch, CURLOPT_HTTPHEADER, [
152
+ 'Accept: application/vnd.github.v3+json',
153
+ 'Authorization: Bearer ' . $token,
154
+ 'User-Agent: ArtiFrame-CLI'
155
+ ]);
156
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
157
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
158
+
159
+ $response = curl_exec($ch);
160
+ curl_close($ch);
161
+
162
+ return $response ?: null;
163
+ }
164
+
165
+ private function wordWrapWithIndentation(string $text, int $width = 80): string
166
+ {
167
+ $lines = explode("\n", $text);
168
+ $wrapped = [];
169
+ foreach ($lines as $line) {
170
+ $wrapped[] = wordwrap(trim($line), $width, "\n");
171
+ }
172
+ return implode("\n", $wrapped);
173
+ }
174
+ }
@@ -14,7 +14,8 @@ class ListCommand
14
14
 
15
15
  public function execute(array $args): void
16
16
  {
17
- $dir = getcwd();
17
+ $safeguard = new \ArtiFrame\Cli\Services\Safeguard($this->translator);
18
+ $dir = $safeguard->getProjectRoot();
18
19
  $projectName = basename($dir);
19
20
 
20
21
  echo "
@@ -38,7 +38,7 @@ class MakeApiCommand
38
38
  $target .= '.php';
39
39
  }
40
40
 
41
- $projectRoot = getcwd();
41
+ $projectRoot = $this->safeguard->getProjectRoot();
42
42
  $apiPath = $projectRoot . '/public/api/' . $type . '/' . $target;
43
43
 
44
44
  if (!$this->safeguard->checkTarget($apiPath)) {
@@ -35,7 +35,7 @@ class MakeClassCommand
35
35
  $target .= '.php';
36
36
  }
37
37
 
38
- $projectRoot = getcwd();
38
+ $projectRoot = $this->safeguard->getProjectRoot();
39
39
  $classPath = $projectRoot . '/' . $target;
40
40
 
41
41
  if (!$this->safeguard->checkTarget($classPath)) {
@@ -32,7 +32,7 @@ class MakeViewCommand
32
32
  $target .= '.php';
33
33
  }
34
34
 
35
- $projectRoot = getcwd();
35
+ $projectRoot = $this->safeguard->getProjectRoot();
36
36
  $viewPath = $projectRoot . '/public/' . $target;
37
37
 
38
38
  if (!$this->safeguard->checkTarget($viewPath)) {
@@ -425,7 +425,11 @@ class NewProjectCommand
425
425
  "Bin\\\\": "bin/",
426
426
  "App\\\\": "app/",
427
427
  "Src\\\\": "src/"
428
- }
428
+ },
429
+ "files": [
430
+ "bin/ViewMethod.php",
431
+ "bin/SystemMethod.php"
432
+ ]
429
433
  },
430
434
  "authors": [
431
435
  {
@@ -469,6 +473,7 @@ JSON;
469
473
  "# Uygulama",
470
474
  "APP_NAME=ArtiFrame",
471
475
  "APP_URL=http://localhost",
476
+ "APP_MAINTENANCE=false",
472
477
  "",
473
478
  "# Veritabanı (MySQL / MariaDB)",
474
479
  "DB_HOST=localhost",
@@ -476,22 +481,7 @@ JSON;
476
481
  "DB_PASS=",
477
482
  "DB_NAME=artiframe",
478
483
  "",
479
- "# SMTP (PHPMailer)",
480
- "MAIL_HOST=smtp.example.com",
481
- "MAIL_PORT=587",
482
- "MAIL_USERNAME=",
483
- "MAIL_PASSWORD=",
484
- "MAIL_FROM_ADDRESS=noreply@example.com",
485
- "MAIL_FROM_NAME=ArtiFrame",
486
- "MAIL_ENCRYPTION=tls",
487
- "",
488
- "# Cloudflare R2 (AWS S3 Uyumlu Depolama)",
489
- "R2_ACCOUNT_ID=",
490
- "R2_ACCESS_KEY=",
491
- "R2_SECRET_KEY=",
492
- "R2_BUCKET_NAME=",
493
- "R2_PUBLIC_URL=",
494
- "",
484
+
495
485
  "# Redis",
496
486
  "REDIS_HOST=127.0.0.1",
497
487
  "REDIS_PORT=6379",
@@ -645,14 +635,14 @@ REDISPHP;
645
635
  // public/index.php — localized welcome page
646
636
  $lang = $this->translator->getLang();
647
637
  $docFile = [
648
- 'tr' => ['file' => 'kilavuz.html', 'installed' => 'Projeniz Başarıyla Oluşturuldu!', 'guide' => 'İlk adımlar için kılavuzu inceleyebilirsiniz:'],
649
- 'en' => ['file' => 'guide.html', 'installed' => 'Project Created Successfully!', 'guide' => 'Check out the guide for the first steps:'],
650
- 'de' => ['file' => 'handbuch.html', 'installed' => 'Projekt Erfolgreich Erstellt!', 'guide' => 'Für die ersten Schritte sehen Sie im Handbuch nach:'],
651
- 'fr' => ['file' => 'guide_fr.html', 'installed' => 'Projet Créé avec Succès !', 'guide' => 'Consultez le guide pour les premières étapes :'],
652
- 'es' => ['file' => 'guia.html', 'installed' => '¡Proyecto Creado Exitosamente!', 'guide' => 'Consulta la guía para los primeros pasos:'],
653
- ][$lang] ?? ['file' => 'guide.html', 'installed' => 'ArtiFrame Installed Successfully!', 'guide' => 'To get started, see'];
654
-
655
- $indexContent = "<?php\nrequire_once __DIR__ . '/../app/ViewControl.php';\n"
638
+ 'tr' => ['file' => 'kilavuz.html', 'installed' => 'Projeniz Başarıyla Oluşturuldu!', 'guide' => 'İlk adımlar için kılavuzu inceleyebilirsiniz:', 'note' => '💡 <b>Not:</b> Eğer anasayfa (landing.php) ismini değiştirmek isterseniz, <code>bin/ViewMethod.php</code> içindeki <code>routeLink()</code> metodunda bulunan varsayılan sayfa adını da güncellemelisiniz.'],
639
+ 'en' => ['file' => 'guide.html', 'installed' => 'Project Created Successfully!', 'guide' => 'Check out the guide for the first steps:', 'note' => '💡 <b>Note:</b> If you want to change the homepage (landing.php) filename, you must also update the default page name in the <code>routeLink()</code> method inside <code>bin/ViewMethod.php</code>.'],
640
+ 'de' => ['file' => 'handbuch.html', 'installed' => 'Projekt Erfolgreich Erstellt!', 'guide' => 'Für die ersten Schritte sehen Sie im Handbuch nach:', 'note' => '💡 <b>Hinweis:</b> Wenn Sie den Dateinamen der Startseite (landing.php) ändern möchten, müssen Sie auch den Standardseitennamen in der Methode <code>routeLink()</code> in <code>bin/ViewMethod.php</code> aktualisieren.'],
641
+ 'fr' => ['file' => 'guide_fr.html', 'installed' => 'Projet Créé avec Succès !', 'guide' => 'Consultez le guide pour les premières étapes :', 'note' => '💡 <b>Remarque :</b> Si vous souhaitez modifier le nom de fichier de la page d\'accueil (landing.php), vous devez également mettre à jour le nom de page par défaut dans la méthode <code>routeLink()</code> dans <code>bin/ViewMethod.php</code>.'],
642
+ 'es' => ['file' => 'guia.html', 'installed' => '¡Proyecto Creado Exitosamente!', 'guide' => 'Consulta la guía para los primeros pasos:', 'note' => '💡 <b>Nota:</b> Si desea cambiar el nombre del archivo de la página de inicio (landing.php), también debe actualizar el nombre de la página predeterminada en el método <code>routeLink()</code> dentro de <code>bin/ViewMethod.php</code>.'],
643
+ ][$lang] ?? ['file' => 'guide.html', 'installed' => 'ArtiFrame Installed Successfully!', 'guide' => 'To get started, see', 'note' => '💡 <b>Note:</b> If you want to change the homepage (landing.php) filename, you must also update the default page name in the <code>routeLink()</code> method inside <code>bin/ViewMethod.php</code>.'];
644
+
645
+ $landingContent = "<?php\n"
656
646
  . "if (isset(\$_GET['guide'])) {\n"
657
647
  . " header('Content-Type: text/html; charset=utf-8');\n"
658
648
  . " readfile(__DIR__ . '/../{$docFile['file']}');\n"
@@ -696,6 +686,12 @@ REDISPHP;
696
686
  . " transition: all 0.3s ease; box-shadow: 0 4px 14px 0 rgba(9, 151, 114, 0.39);\n"
697
687
  . " }\n"
698
688
  . " .btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(9, 151, 114, 0.45); }\n"
689
+ . " .note {\n"
690
+ . " font-size: 0.85rem; color: #6b7280; margin-top: 2rem;\n"
691
+ . " background: rgba(255,255,255,0.5); padding: 1rem; border-radius: 0.75rem;\n"
692
+ . " border: 1px solid rgba(0,0,0,0.05); text-align: left; line-height: 1.5;\n"
693
+ . " }\n"
694
+ . " .note code { background: rgba(0,0,0,0.05); padding: 0.2rem 0.4rem; border-radius: 4px; font-family: monospace; }\n"
699
695
  . " .footer {\n"
700
696
  . " display: flex; align-items: center; gap: 0.5rem;\n"
701
697
  . " color: #6b7280; font-size: 0.875rem; font-weight: 500;\n"
@@ -714,6 +710,7 @@ REDISPHP;
714
710
  . " <h1>{$docFile['installed']}</h1>\n"
715
711
  . " <p>{$docFile['guide']}</p>\n"
716
712
  . " <a href=\"?guide\" class=\"btn\">{$docFile['file']}</a>\n"
713
+ . " <div class=\"note\">{$docFile['note']}</div>\n"
717
714
  . " </div>\n"
718
715
  . " <div class=\"footer\">\n"
719
716
  . " <span>Powered by</span>\n"
@@ -723,9 +720,24 @@ REDISPHP;
723
720
  . " </div>\n"
724
721
  . "</body>\n"
725
722
  . "</html>\n";
723
+ file_put_contents($targetDir . '/public/landing.php', $landingContent);
724
+ $this->tick('public/landing.php');
725
+
726
+ // public/index.php - router
727
+ $indexContent = "<?php\n\nrequire_once __DIR__ . '/../vendor/autoload.php';\n\nrouteLink();\n";
726
728
  file_put_contents($targetDir . '/public/index.php', $indexContent);
727
729
  $this->tick('public/index.php');
728
730
 
731
+ // public/404.php
732
+ $error404Content = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>404 Not Found</title>\n <style>body{font-family:sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;background:#f3f4f6;color:#374151;text-align:center;} h1{font-size:3rem;margin-bottom:0;} p{font-size:1.2rem;color:#6b7280;}</style>\n</head>\n<body>\n <div>\n <h1>404</h1>\n <p>Page Not Found</p>\n </div>\n</body>\n</html>\n";
733
+ file_put_contents($targetDir . '/public/404.php', $error404Content);
734
+ $this->tick('public/404.php');
735
+
736
+ // public/503.php
737
+ $error503Content = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>503 Service Unavailable</title>\n <style>body{font-family:sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;background:#f3f4f6;color:#374151;text-align:center;} h1{font-size:3rem;margin-bottom:0;} p{font-size:1.2rem;color:#6b7280;}</style>\n</head>\n<body>\n <div>\n <h1>503</h1>\n <p>Service Unavailable / Maintenance Mode</p>\n </div>\n</body>\n</html>\n";
738
+ file_put_contents($targetDir . '/public/503.php', $error503Content);
739
+ $this->tick('public/503.php');
740
+
729
741
  // public/.htaccess
730
742
  $htaccessContent = <<<HTACCESS
731
743
  RewriteEngine On