@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.
- package/core-stubs/bin/SystemMethod.php +600 -521
- package/core-stubs/bin/ViewMethod.php +591 -393
- package/core-stubs/docs/de.html +853 -1172
- package/core-stubs/docs/en.html +585 -904
- package/core-stubs/docs/es.html +934 -1253
- package/core-stubs/docs/fr.html +913 -1232
- package/core-stubs/docs/tr.html +4212 -5021
- package/package.json +1 -1
- package/src/App.php +38 -0
- package/src/Commands/AddCommand.php +1 -1
- package/src/Commands/AuthCommand.php +219 -0
- package/src/Commands/IssuesCommand.php +174 -0
- package/src/Commands/ListCommand.php +2 -1
- package/src/Commands/MakeApiCommand.php +1 -1
- package/src/Commands/MakeClassCommand.php +1 -1
- package/src/Commands/MakeViewCommand.php +1 -1
- package/src/Commands/NewProjectCommand.php +37 -25
- package/src/Commands/RemoveCommand.php +21 -10
- package/src/Commands/ServeCommand.php +29 -9
- package/src/Commands/SuggestCommand.php +143 -0
- package/src/Commands/TableCommand.php +2 -1
- package/src/Commands/UpgradeCommand.php +209 -0
- package/src/Commands/VersionCommand.php +2 -1
- package/src/Lang/de.php +9 -1
- package/src/Lang/en.php +8 -0
- package/src/Lang/es.php +9 -1
- package/src/Lang/fr.php +8 -0
- package/src/Lang/tr.php +10 -2
- package/src/Services/Safeguard.php +25 -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/sentry.stub +76 -106
- package/stubs/service/stripe.stub +526 -526
- package/stubs/service/twilio.stub +361 -361
- 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": "
|
|
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;
|
|
@@ -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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
-
$
|
|
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
|