Skip to content

Commit e1fc717

Browse files
authored
Merge pull request #6 from ploi/develop
Added site option to ssh command & Added domain suggestions (typo) & Small bug fixes
2 parents 3af1da5 + a999e1d commit e1fc717

File tree

6 files changed

+139
-25
lines changed

6 files changed

+139
-25
lines changed

app/Commands/Concerns/InteractWithServer.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ protected function selectServer(): string
1111
{
1212
$servers = $this->ploi->getServerList()['data'];
1313

14-
if (! $servers) {
14+
if (!$servers) {
1515
$this->error('No servers found! Please create a server first.');
1616
exit(1);
1717
}
@@ -29,7 +29,7 @@ protected function selectServer(): string
2929

3030
protected function getServerId(): int
3131
{
32-
if ($this->hasPloiConfigurationFile() && ! $this->option('server')) {
32+
if ($this->hasPloiConfigurationFile() && !$this->option('server')) {
3333
return $this->configuration->get('settings.server');
3434
}
3535

@@ -42,9 +42,12 @@ protected function getServerIdByNameOrIp(string $identifier): ?int
4242
{
4343
$servers = collect($this->ploi->getServerList(search: $identifier)['data']);
4444

45-
$server = $servers->first(fn ($server) => $server['name'] === $identifier || $server['ip_address'] === $identifier);
45+
$server = $servers->first(fn ($server) =>
46+
$server['name'] === $identifier ||
47+
$server['ip_address'] === $identifier
48+
);
4649

47-
if (! $server) {
50+
if (!$server) {
4851
$this->error("Server with name or IP '{$identifier}' not found.");
4952
exit(1);
5053
}
@@ -78,9 +81,8 @@ protected function pollServerStatus(string $serverId): ?bool
7881
message: 'Waiting for the server to be created...'
7982
);
8083

81-
if (! $isCreated) {
84+
if (!$isCreated) {
8285
$this->error('Server creation timed out.');
83-
8486
return false;
8587
}
8688

@@ -100,14 +102,12 @@ protected function pollServerStatus(string $serverId): ?bool
100102
message: 'Waiting for the server to finish building...'
101103
);
102104

103-
if (! $isBuilding) {
105+
if (!$isBuilding) {
104106
$this->error('Server building timed out.');
105-
106107
return false;
107108
}
108109

109110
$this->success('Server is ready!');
110-
111111
return true;
112112
}
113113
}

app/Commands/Concerns/InteractWithSite.php

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,31 @@
22

33
namespace App\Commands\Concerns;
44

5+
use App\Traits\DomainSuggestions;
56
use function Laravel\Prompts\select;
67

78
trait InteractWithSite
89
{
10+
use DomainSuggestions;
11+
912
protected function getServerAndSite(): array
1013
{
11-
if ($this->hasPloiConfigurationFile() && ! $this->option('server') && ! $this->option('site')) {
14+
if ($this->hasPloiConfigurationFile() &&
15+
!$this->option('server') &&
16+
!$this->option('site')) {
1217
$serverId = $this->configuration->get('settings.server');
1318
$siteId = $this->configuration->get('settings.site');
1419
} else {
15-
$serverIdentifier = $this->option('server') ?? $this->selectServer();
20+
$serverIdentifier = $this->option('server')
21+
?? $this->selectServer();
1622
$serverId = $this->getServerIdByNameOrIp($serverIdentifier);
1723

18-
$siteIdentifier = $this->option('site') ?? $this->selectSite($serverId)['domain'];
24+
$siteIdentifier = $this->option('site')
25+
?? $this->selectSite($serverId)['domain'];
1926
$siteId = $this->getSiteIdByDomain($serverId, $siteIdentifier);
2027
}
2128

22-
if (! $serverId || ! $siteId) {
29+
if (!$serverId || !$siteId) {
2330
$this->error('Server and site must be valid.');
2431
exit(1);
2532
}
@@ -29,7 +36,9 @@ protected function getServerAndSite(): array
2936

3037
protected function selectSite($serverId): array
3138
{
32-
$sites = collect($this->ploi->getSiteList($serverId)['data'])->pluck('domain', 'domain')->toArray();
39+
$sites = collect($this->ploi->getSiteList($serverId)['data'])
40+
->pluck('domain', 'domain')
41+
->toArray();
3342
$domain = select('Select a site by domain:', $sites, scroll: 10);
3443

3544
return ['domain' => $domain];
@@ -38,12 +47,19 @@ protected function selectSite($serverId): array
3847
protected function getSiteIdByDomain(int $serverId, string $domain): ?int
3948
{
4049
$sites = collect($this->ploi->getSiteList($serverId)['data']);
50+
$availableDomains = $sites->pluck('domain')->toArray();
4151

4252
$site = $sites->first(fn ($site) => $site['domain'] === $domain);
4353

44-
if (! $site) {
45-
$this->error("Site with domain '{$domain}' not found on the selected server.");
46-
exit(1);
54+
if (!$site) {
55+
$selectedDomain = $this->getDomainSuggestionsWithSelection($domain, $availableDomains);
56+
57+
if ($selectedDomain) {
58+
$site = $sites->first(fn ($site) => $site['domain'] === $selectedDomain);
59+
} else {
60+
$this->error("Site with domain '{$domain}' not found on the selected server.");
61+
exit(1);
62+
}
4763
}
4864

4965
return $site['id'];

app/Commands/Server/RestartServerCommand.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ public function handle(): void
2222
$serverId = $this->getServerId();
2323

2424
try {
25-
$restartServerResponse = $this->ploi->restartServer($serverId);
25+
$restartServerResponse = $this->ploi->restartServer($serverId)['data'];
2626

27-
$this->success($restartServerResponse['message']);
27+
$this->success($restartServerResponse[0]['message']);
2828
} catch (\Exception $e) {
2929
$this->error($e->getMessage());
3030
}

app/Commands/SshLoginCommand.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Commands;
44

55
use App\Commands\Concerns\InteractWithServer;
6+
use App\Commands\Concerns\InteractWithSite;
67
use App\Traits\EnsureHasToken;
78
use App\Traits\HasPloiConfiguration;
89
use Illuminate\Support\Facades\Process;
@@ -11,26 +12,47 @@
1112

1213
class SshLoginCommand extends Command
1314
{
14-
use EnsureHasToken, HasPloiConfiguration, InteractWithServer;
15+
use EnsureHasToken, HasPloiConfiguration, InteractWithServer, InteractWithSite;
1516

16-
protected $signature = 'ssh {--server=} {--u|user=}';
17+
protected $signature = 'ssh {--server=} {--site=} {--u|user=}';
1718

1819
protected $description = 'Login to your server via SSH';
1920

2021
protected array $server = [];
2122

23+
protected array $site = [];
24+
2225
public function handle(): void
2326
{
2427
$this->ensureHasToken();
2528

26-
$serverId = $this->getServerId();
29+
[$serverId, $siteId] = $this->getServerAndSite();
2730

2831
$this->server = $this->ploi->getServerDetails($serverId)['data'];
32+
$this->site = $this->ploi->getSiteDetails($serverId, $siteId)['data'];
2933

30-
$user = $this->option('user') ?? text(label: 'Enter the username you want to login with', default: 'ploi');
34+
$user = $this->determineUser();
35+
$user = text(
36+
label: 'Enter the username you want to login with',
37+
default: $user,
38+
required: true
39+
);
3140

3241
$this->info("Logging in to {$this->server['name']} as {$user}");
3342

3443
Process::forever()->tty()->run("ssh {$user}@{$this->server['ip_address']}");
3544
}
45+
46+
protected function determineUser(): string
47+
{
48+
if ($this->option('user')) {
49+
return $this->option('user');
50+
}
51+
52+
if (!empty($this->site)) {
53+
return $this->site['system_user'];
54+
}
55+
56+
return text(label: 'Enter the username you want to login with', default: 'ploi');
57+
}
3658
}

app/Support/Configuration.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ protected function getProvisionFormat(): array
5959

6060
public function store(string $configFile, string $type): void
6161
{
62-
// Wrap the content in the type key when storing
62+
$directory = dirname($configFile);
63+
if (!is_dir($directory)) {
64+
mkdir($directory, 0755, true);
65+
}
66+
6367
$wrappedConfig = [
6468
$type => $this->configs[$type]
6569
];
@@ -121,4 +125,4 @@ public function getConfigTypes(): array
121125
{
122126
return array_keys($this->configFiles);
123127
}
124-
}
128+
}

app/Traits/DomainSuggestions.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace App\Traits;
4+
5+
use function Laravel\Prompts\select;
6+
7+
trait DomainSuggestions
8+
{
9+
/**
10+
* Get domain suggestions based on user input with interactive selection
11+
*
12+
* @param string $input User input domain
13+
* @param array $availableDomains List of available domains
14+
* @param int $maxSuggestions Maximum number of suggestions to return
15+
* @return string|null Selected domain or null if no selection made
16+
*/
17+
protected function getDomainSuggestionsWithSelection(string $input, array $availableDomains, int $maxSuggestions = 5): ?string
18+
{
19+
$suggestions = [];
20+
21+
foreach ($availableDomains as $domain) {
22+
$similarity = $this->calculateSimilarity($input, $domain);
23+
24+
if ($similarity > 0.6) {
25+
$suggestions[] = [
26+
'domain' => $domain,
27+
'similarity' => $similarity
28+
];
29+
}
30+
}
31+
32+
usort($suggestions, fn($a, $b) => $b['similarity'] <=> $a['similarity']);
33+
$suggestions = array_slice($suggestions, 0, $maxSuggestions);
34+
35+
if (empty($suggestions)) {
36+
return null;
37+
}
38+
39+
$options = [];
40+
foreach ($suggestions as $suggestion) {
41+
$options[$suggestion['domain']] = $suggestion['domain'];
42+
}
43+
$options['none'] = 'None of these';
44+
45+
$selected = select(
46+
label: "Domain '{$input}' not found. Did you mean:",
47+
options: $options,
48+
scroll: min(count($options), 10)
49+
);
50+
51+
return $selected === 'none' ? null : $selected;
52+
}
53+
54+
/**
55+
* Calculate similarity between two strings
56+
* Uses a combination of algorithms for better matching
57+
*/
58+
protected function calculateSimilarity(string $str1, string $str2): float
59+
{
60+
$str1 = strtolower($str1);
61+
$str2 = strtolower($str2);
62+
63+
$levDistance = levenshtein($str1, $str2);
64+
$maxLength = max(strlen($str1), strlen($str2));
65+
$levSimilarity = 1 - ($levDistance / $maxLength);
66+
67+
similar_text($str1, $str2, $percentage);
68+
$textSimilarity = $percentage / 100;
69+
70+
return ($levSimilarity * 0.6) + ($textSimilarity * 0.4);
71+
}
72+
}

0 commit comments

Comments
 (0)