Example code
The code for dns integrations can be found in:
app/Framework/Core/Hosting/Ipam\DnsProviderFor each dns provider, you have a php class, for example the cloudflare.php. To get started with a new dns provider, you can just copy one and remove the code.
Example (cloudflare)
<?php
namespace App\Framework\Core\Hosting\Ipam\DnsProvider;
use App\Framework\Core\Hosting\Domain\DnsRecordItem;
use App\Framework\Core\Hosting\Ipam\DnsProvider\Responses\ZoneDetails;
use App\Framework\Core\Hosting\Ipam\Models\IpamRdnsHost;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Psr7\Request;
use Illuminate\Support\Facades\Cache;
class cloudflare extends AbstractDnsProvider implements DnsProviderInterface {
/**
* @var Client
*/
private $guzzleClient;
public array $availableTypes = [
"A" => "A",
"AAAA" => "AAAA",
"CNAME" => "CNAME",
"MX" => "MX",
"DKIM" => "DKIM",
"SPF" => "SPF",
"DMARC" => "DMARC",
"TXT" => "TXT",
"CAA" => "CAA",
"SRV" => "SRV",
"SVCB" => "SVCB",
"HTTPS" => "HTTPS",
"PTR" => "PTR",
"NS" => "NS",
];
public function createZone(IpamRdnsHost $ipamRdnsHost, string $zoneName, ?array $nameservers = null): ZoneDetails {
try {
$response = $this->_sendRequest($ipamRdnsHost, "zones", "POST", [
"name" => $this->getZoneName($zoneName, $ipamRdnsHost, false),
"type" => "full",
]);
return new ZoneDetails(
$response->result->id,
$response->result->name,
null,
$response->result->name_servers
);
} catch (ClientException $e) {
throw new Exception($this->_getErrorMessage($e));
}
}
public function getAllZones(IpamRdnsHost $ipamRdnsHost): array {
$return = [];
try {
$zoneResponse = $this->_sendRequest($ipamRdnsHost, "zones?per_page=50");
} catch (ClientException $e) {
throw new Exception($this->_getErrorMessage($e));
}
foreach ($zoneResponse->result as $zone) {
$return[] = new ZoneDetails(
$zone->id,
$zone->name,
null,
$zone->name_servers
);
}
Cache::put("cfa", $zoneResponse->result[0]?->account?->id, 3600);
return $return;
}
public function deleteZone(IpamRdnsHost $ipamRdnsHost, string $zoneName): bool {
$zoneName = $this->getZoneName($zoneName, $ipamRdnsHost);
// try {
// $this->_sendRequest($ipamRdnsHost, "servers/localhost/zones/{$zoneName}", "DELETE");
//
// return true;
// } catch (ClientException $e) {
// throw new Exception($e->getResponse()->getBody());
// }
}
public function getZoneDetails(IpamRdnsHost $ipamRdnsHost, string $zoneName): ZoneDetails {
$dnsRecords = [];
$zoneName = $this->getZoneName($zoneName, $ipamRdnsHost);
try {
$zoneResponse = $this->_sendRequest($ipamRdnsHost, "zones/{$zoneName}");
$recordsResponse = $this->_sendRequest($ipamRdnsHost, "zones/{$zoneName}/dns_records");
} catch (ClientException $e) {
throw new Exception($this->_getErrorMessage($e));
}
foreach ($recordsResponse->result as $record) {
$dnsRecords[] = new DnsRecordItem(
$record->id,
$record->type,
$record->name,
$record->content,
$record->ttl,
);
}
return new ZoneDetails(
$zoneResponse->result->id,
$zoneResponse->result->name,
null,
$zoneResponse->result->name_servers,
$dnsRecords
);
}
public function createDnsRecord(IpamRdnsHost $ipamRdnsHost, string $zoneName, DnsRecordItem $dnsRecord) {
$zoneName = $this->getZoneName($zoneName, $ipamRdnsHost);
try {
$response = $this->_sendRequest($ipamRdnsHost, "zones/{$zoneName}/dns_records", "POST", [
"content" => $dnsRecord->content,
"name" => $dnsRecord->hostname,
"type" => $dnsRecord->type,
"ttl" => $dnsRecord->ttl,
]);
return $response->result->id;
} catch (ClientException $e) {
throw new Exception($this->_getErrorMessage($e));
}
}
public function deleteDnsRecord(IpamRdnsHost $ipamRdnsHost, string $zoneName, DnsRecordItem $dnsRecord) {
$zoneName = $this->getZoneName($zoneName, $ipamRdnsHost);
try {
$this->_sendRequest($ipamRdnsHost, "zones/{$zoneName}/dns_records/{$dnsRecord->id}", "DELETE");
} catch (ClientException $e) {
throw new Exception($this->_getErrorMessage($e));
}
}
public function updateDnsRecord(IpamRdnsHost $ipamRdnsHost, string $zoneName, DnsRecordItem $originalDnsRecord, DnsRecordItem $updatedDnsRecord) {
$zoneName = $this->getZoneName($zoneName, $ipamRdnsHost);
try {
$this->_sendRequest($ipamRdnsHost, "zones/{$zoneName}/dns_records/{$originalDnsRecord->id}", "PUT", [
"content" => $updatedDnsRecord->content,
"name" => $updatedDnsRecord->hostname,
"type" => $updatedDnsRecord->type,
"ttl" => $updatedDnsRecord->ttl,
]);
} catch (ClientException $e) {
throw new Exception($this->_getErrorMessage($e));
}
}
private function _sendRequest(IpamRdnsHost $ipamRdnsHost, string $url, string $method = "GET", array $data = []) {
$request = new Request($method, $url, [], json_encode($data));
$res = $this->_getClient($ipamRdnsHost)->sendAsync($request)->wait();
return json_decode($res->getBody()->getContents());
}
private function _getClient(IpamRdnsHost $ipamRdnsHost) {
if ($this->guzzleClient === null) {
$this->guzzleClient = new Client([
'verify' => false,
'base_uri' => "https://api.cloudflare.com/client/v4/",
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $ipamRdnsHost->password,
]
]);
}
return $this->guzzleClient;
}
public function getZoneDetailUrl(IpamRdnsHost $ipamRdnsHost, string $zoneName) {
$accountId = Cache::get("cfa", "n-a");
return "https://dash.cloudflare.com/{$accountId}/{$zoneName}";
}
private function _getErrorMessage(Exception $e): string {
$content = json_decode((string)$e->getResponse()->getBody());
if ($content->success === false) {
return implode(", ", array_column($content->errors, "message"));
}
return "Successful response.";
}
public function getZoneName($zoneName, IpamRdnsHost $rdnsHost, $searchId = true) {
if(str_ends_with($zoneName, ".")) {
$zoneName = trim($zoneName, ".");
}
/**
* For rdns, search the zone and return the ID
*/
if(str_contains($zoneName, "in-addr.arpa") && $searchId === true) {
try {
$response = $this->_sendRequest($rdnsHost, "zones?name={$zoneName}", "GET");
if(isset($response->result[0])) {
$zoneName = $response->result[0]->id;
}
} catch (Exception $e) {
}
}
return $zoneName;
}
}Was this helpful?