'Cloudflare', 'doppler' => 'Doppler', 'infisical' => 'Infisical', 'vault' => 'HashiCorp Vault', ]; protected $fillable = [ 'team_id', 'provider', 'name', 'token', 'capabilities', 'metadata', ]; protected $hidden = [ 'token', ]; protected function casts(): array { return [ 'token' => 'encrypted', 'capabilities' => 'array', 'metadata' => 'array', ]; } public function team(): BelongsTo { return $this->belongsTo(Team::class); } public function secretManagerLinks(): HasMany { return $this->hasMany(SecretManagerLink::class); } public function dnsZones(): HasMany { return $this->hasMany(DnsProviderZone::class); } public function managedDnsRecords(): HasMany { return $this->hasMany(ManagedDnsRecord::class); } public function isSecretManager(): bool { return in_array($this->provider, self::SECRET_MANAGER_PROVIDERS, true); } public function providerName(): string { return self::PROVIDER_NAMES[$this->provider] ?? ucfirst($this->provider); } /** * Capabilities a token of the provider can have. Cloudflare Tunnel setup also writes DNS records, so `tunnel` needs `dns`. * * @return array */ public static function allowedCapabilities(string $provider): array { return $provider === 'cloudflare' ? ['dns', 'tunnel'] : ['secrets']; } /** * Cloudflare dashboard link that pre-fills a user API token with the permissions the capabilities need. * * @param array $capabilities */ public static function cloudflareTokenTemplateUrl(array $capabilities): string { $permissions = [ ['key' => 'dns', 'type' => 'edit'], ['key' => 'zone', 'type' => 'read'], ]; if (in_array('tunnel', $capabilities, true)) { // `argotunnel` is Cloudflare's internal key for the "Cloudflare Tunnel" permission group. $permissions[] = ['key' => 'argotunnel', 'type' => 'edit']; } return 'https://dash.cloudflare.com/profile/api-tokens?'.http_build_query([ 'permissionGroupKeys' => json_encode($permissions), 'accountId' => '*', 'zoneId' => 'all', 'name' => in_array('tunnel', $capabilities, true) ? 'Coolify DNS and Tunnel Management' : 'Coolify DNS Management', ], '', '&', PHP_QUERY_RFC3986); } public function hasCapability(string $capability): bool { return in_array($capability, $this->capabilities ?? [], true); } public function automaticDnsEnabled(): bool { return $this->provider === 'cloudflare' && data_get($this->metadata, 'automatic_dns', true) !== false; } /** New Cloudflare records are created proxied (orange cloud) when enabled, so the server IP is not exposed. */ public function proxiedDnsEnabled(): bool { return $this->provider === 'cloudflare' && data_get($this->metadata, 'proxied_dns') === true; } public function dopplerTokenType(): ?string { if ($this->provider !== 'doppler') { return null; } return match (true) { str_starts_with($this->token, 'dp.st.') => 'service', str_starts_with($this->token, 'dp.sa.') => 'service_account', default => null, }; } public static function ownedByCurrentTeam() { return self::query()->where('team_id', currentTeam()->id); } }