mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
100 lines
3.9 KiB
PHP
100 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\IntegrationToken;
|
|
|
|
use App\Models\IntegrationToken;
|
|
use App\Services\Dns\CloudflareDnsProvider;
|
|
use App\Services\IntegrationTokenValidator;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Lorisleiva\Actions\Concerns\AsAction;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* Creates an integration token after the provider accepted it. A Cloudflare token also gets its zones in the same
|
|
* transaction, so a token without zone access is never stored. The security page and the API use this action.
|
|
*/
|
|
class CreateIntegrationToken
|
|
{
|
|
use AsAction;
|
|
|
|
public function __construct(
|
|
private IntegrationTokenValidator $validator,
|
|
private CloudflareDnsProvider $cloudflare,
|
|
) {}
|
|
|
|
/**
|
|
* Validation rules for the token fields. `automatic_dns` and `proxied_dns` are validated by the caller.
|
|
*
|
|
* @return array<string, array<int, mixed>>
|
|
*/
|
|
public static function rules(string $provider): array
|
|
{
|
|
$rules = [
|
|
'provider' => ['required', 'in:'.implode(',', array_keys(IntegrationToken::PROVIDER_NAMES))],
|
|
'name' => ['required', 'string', 'max:255'],
|
|
'token' => ['required', 'string'],
|
|
'capabilities' => ['required', 'array', 'min:1', function (string $attribute, mixed $value, \Closure $fail): void {
|
|
if (is_array($value) && in_array('tunnel', $value, true) && ! in_array('dns', $value, true)) {
|
|
$fail('Cloudflare Tunnel also needs the DNS capability.');
|
|
}
|
|
}],
|
|
'capabilities.*' => ['required', 'in:'.implode(',', IntegrationToken::allowedCapabilities($provider))],
|
|
];
|
|
|
|
if ($provider === 'doppler') {
|
|
$rules['token'][] = 'regex:/^dp\.(st|sa)\./';
|
|
} elseif ($provider === 'infisical') {
|
|
$rules['metadata.base_url'] = ['required', 'url:http,https'];
|
|
$rules['metadata.client_id'] = ['required', 'string'];
|
|
$rules['metadata.timeout'] = ['nullable', 'integer', 'min:1', 'max:300'];
|
|
} elseif ($provider === 'vault') {
|
|
$rules['metadata.base_url'] = ['required', 'url:http,https'];
|
|
$rules['metadata.namespace'] = ['nullable', 'string'];
|
|
$rules['metadata.timeout'] = ['nullable', 'integer', 'min:1', 'max:300'];
|
|
}
|
|
|
|
return $rules;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, string> $capabilities
|
|
* @param array<string, mixed> $metadata Provider settings (base URL, client ID, ...); empty values are dropped.
|
|
*
|
|
* @throws RuntimeException when the provider rejects the token or Cloudflare zones cannot be read
|
|
*/
|
|
public function handle(
|
|
int $teamId,
|
|
string $provider,
|
|
string $name,
|
|
string $token,
|
|
array $capabilities,
|
|
array $metadata = [],
|
|
bool $automaticDns = true,
|
|
bool $proxiedDns = false,
|
|
): IntegrationToken {
|
|
$metadata = array_filter($metadata, fn ($value) => filled($value));
|
|
if ($provider === 'cloudflare' && ! $automaticDns) {
|
|
$metadata['automatic_dns'] = false;
|
|
}
|
|
if ($provider === 'cloudflare' && $proxiedDns) {
|
|
$metadata['proxied_dns'] = true;
|
|
}
|
|
|
|
if (! $this->validator->validate($provider, $token, $capabilities, $metadata)) {
|
|
throw new RuntimeException($this->validator->errorMessage($provider));
|
|
}
|
|
|
|
return DB::transaction(function () use ($teamId, $provider, $name, $token, $capabilities, $metadata): IntegrationToken {
|
|
$integrationToken = IntegrationToken::query()->create([
|
|
'team_id' => $teamId, 'provider' => $provider, 'name' => $name, 'token' => $token,
|
|
'capabilities' => $capabilities, 'metadata' => $metadata ?: null,
|
|
]);
|
|
if ($provider === 'cloudflare') {
|
|
$this->cloudflare->syncZones($integrationToken);
|
|
}
|
|
|
|
return $integrationToken;
|
|
});
|
|
}
|
|
}
|