-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Expand file tree
/
Copy pathGetProxyConfiguration.php
More file actions
127 lines (104 loc) · 4.7 KB
/
Copy pathGetProxyConfiguration.php
File metadata and controls
127 lines (104 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php
namespace App\Actions\Proxy;
use App\Enums\ProxyTypes;
use App\Models\Server;
use App\Services\ProxyDashboardCacheService;
use Illuminate\Support\Facades\Log;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class GetProxyConfiguration
{
use AsAction;
public const MAX_CONFIGURATION_SIZE_BYTES = 5 * 1024 * 1024;
public function handle(Server $server, bool $forceRegenerate = false): string
{
$proxyType = $server->proxyType();
if ($proxyType === 'NONE') {
return 'OK';
}
$proxy_configuration = null;
if (! $forceRegenerate) {
// Primary source: database
$proxy_configuration = $server->proxy->get('last_saved_proxy_configuration');
// Validate stored config matches current proxy type
if (! empty(trim($proxy_configuration ?? ''))) {
if (! $this->configMatchesProxyType($proxyType, $proxy_configuration)) {
Log::warning('Stored proxy config does not match current proxy type, will regenerate', [
'server_id' => $server->id,
'proxy_type' => $proxyType,
]);
$proxy_configuration = null;
}
}
// Backfill: existing servers may not have DB config yet — read from disk once
if (empty(trim($proxy_configuration ?? ''))) {
$proxy_configuration = $this->backfillFromDisk($server);
}
}
// Generate default configuration as last resort
if ($forceRegenerate || empty(trim($proxy_configuration ?? ''))) {
$custom_commands = [];
if (! empty(trim($proxy_configuration ?? ''))) {
$custom_commands = extractCustomProxyCommands($server, $proxy_configuration);
}
Log::warning('Proxy configuration regenerated to defaults', [
'server_id' => $server->id,
'server_name' => $server->name,
'reason' => $forceRegenerate ? 'force_regenerate' : 'config_not_found',
]);
$proxy_configuration = str(generateDefaultProxyConfiguration($server, $custom_commands))->trim()->value();
}
if (empty($proxy_configuration)) {
throw new \Exception('Could not get or generate proxy configuration');
}
ProxyDashboardCacheService::isTraefikDashboardAvailableFromConfiguration($server, $proxy_configuration);
return $proxy_configuration;
}
/**
* Check that the stored docker-compose YAML contains the expected service
* for the server's current proxy type. Returns false if the config belongs
* to a different proxy type (e.g. Traefik config on a CADDY server).
*/
private function configMatchesProxyType(string $proxyType, string $configuration): bool
{
try {
$yaml = Yaml::parse($configuration);
$services = data_get($yaml, 'services', []);
return match ($proxyType) {
ProxyTypes::TRAEFIK->value => isset($services['traefik']),
ProxyTypes::CADDY->value => isset($services['caddy']),
ProxyTypes::NGINX->value => isset($services['nginx']),
default => true,
};
} catch (\Throwable $e) {
// If YAML is unparseable, don't block — let the existing flow handle it
return true;
}
}
/**
* Backfill: read config from disk for servers that predate DB storage.
* Stores the result in the database so future reads skip SSH entirely.
*/
private function backfillFromDisk(Server $server): ?string
{
$proxy_path = $server->proxyPath();
$configurationPath = escapeshellarg("$proxy_path/docker-compose.yml");
$readLimit = self::MAX_CONFIGURATION_SIZE_BYTES + 1;
$result = instant_remote_process([
"mkdir -p $proxy_path",
"if [ ! -f {$configurationPath} ]; then exit 0; elif [ \"$(wc -c < {$configurationPath})\" -gt ".self::MAX_CONFIGURATION_SIZE_BYTES." ]; then echo '__COOLIFY_PROXY_CONFIG_TOO_LARGE__'; else head -c {$readLimit} {$configurationPath}; fi",
], $server, false);
if ($result === '__COOLIFY_PROXY_CONFIG_TOO_LARGE__') {
throw new \RuntimeException('Proxy configuration exceeds the 5 MiB size limit.');
}
if (! empty(trim($result ?? ''))) {
$server->proxy->last_saved_proxy_configuration = $result;
$server->save();
Log::info('Proxy config backfilled to database from disk', [
'server_id' => $server->id,
]);
return $result;
}
return null;
}
}