refactor(deployment): Remove WireGuard VPN dependency and restore public service access

Remove WireGuard integration from production deployment to simplify infrastructure:
- Remove docker-compose-direct-access.yml (VPN-bound services)
- Remove VPN-only middlewares from Grafana, Prometheus, Portainer
- Remove WireGuard middleware definitions from Traefik
- Remove WireGuard IPs (10.8.0.0/24) from Traefik forwarded headers

All monitoring services now publicly accessible via subdomains:
- grafana.michaelschiemer.de (with Grafana native auth)
- prometheus.michaelschiemer.de (with Basic Auth)
- portainer.michaelschiemer.de (with Portainer native auth)

All services use Let's Encrypt SSL certificates via Traefik.
This commit is contained in:
2025-11-05 12:48:25 +01:00
parent 7c52065aae
commit 95147ff23e
215 changed files with 29490 additions and 368 deletions

View File

@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\Api\Netcup;
use App\Framework\Http\Method;
use App\Infrastructure\Api\Netcup\ValueObjects\DnsRecord;
final readonly class DnsService
{
public function __construct(
private NetcupApiClient $apiClient
) {
}
/**
* Listet alle DNS-Records einer Domain auf
*
* @param string $domain Die Domain (z.B. 'example.com')
* @return array Liste der DnsRecord Value Objects
*/
public function listRecords(string $domain): array
{
$response = $this->apiClient->request(
Method::GET,
"dns/{$domain}/records"
);
$records = $response['records'] ?? $response;
if (! is_array($records)) {
return [];
}
return array_map(
fn (array $data) => DnsRecord::fromArray($data),
$records
);
}
/**
* Ruft einen einzelnen DNS-Record ab
*
* @param string $domain Die Domain
* @param string $recordId Die Record-ID
* @return DnsRecord DNS-Record Value Object
*/
public function getRecord(string $domain, string $recordId): DnsRecord
{
$response = $this->apiClient->request(
Method::GET,
"dns/{$domain}/records/{$recordId}"
);
$data = $response['record'] ?? $response;
return DnsRecord::fromArray($data);
}
/**
* Erstellt einen neuen DNS-Record
*
* @param string $domain Die Domain
* @param DnsRecord $record DNS-Record Value Object
* @return DnsRecord Erstellter DNS-Record
*/
public function createRecord(string $domain, DnsRecord $record): DnsRecord
{
$response = $this->apiClient->request(
Method::POST,
"dns/{$domain}/records",
$record->toArray()
);
$data = $response['record'] ?? $response;
return DnsRecord::fromArray($data);
}
/**
* Aktualisiert einen DNS-Record
*
* @param string $domain Die Domain
* @param string $recordId Die Record-ID
* @param DnsRecord $record DNS-Record Value Object
* @return DnsRecord Aktualisierter DNS-Record
*/
public function updateRecord(string $domain, string $recordId, DnsRecord $record): DnsRecord
{
$data = $record->toArray();
unset($data['id']); // Remove ID from update payload
$response = $this->apiClient->request(
Method::PUT,
"dns/{$domain}/records/{$recordId}",
$data
);
$responseData = $response['record'] ?? $response;
return DnsRecord::fromArray($responseData);
}
/**
* Löscht einen DNS-Record
*
* @param string $domain Die Domain
* @param string $recordId Die Record-ID
* @return void
*/
public function deleteRecord(string $domain, string $recordId): void
{
$this->apiClient->sendRawRequest(
Method::DELETE,
"dns/{$domain}/records/{$recordId}"
);
}
}

View File

@@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\Api\Netcup;
use App\Framework\Api\ApiException;
use App\Framework\Http\Method;
use App\Framework\HttpClient\AuthConfig;
use App\Framework\HttpClient\ClientOptions;
use App\Framework\HttpClient\ClientRequest;
use App\Framework\HttpClient\ClientResponse;
use App\Framework\HttpClient\HttpClient;
final readonly class NetcupApiClient
{
private ClientOptions $defaultOptions;
public function __construct(
private NetcupConfig $config,
private HttpClient $httpClient
) {
$this->defaultOptions = new ClientOptions(
timeout: (int) $this->config->timeout,
auth: $this->buildAuthConfig()
);
}
/**
* Sendet eine API-Anfrage und gibt JSON-Daten zurück
*/
public function request(
Method $method,
string $endpoint,
array $data = [],
array $queryParams = []
): array {
$response = $this->sendRawRequest($method, $endpoint, $data, $queryParams);
return $this->handleResponse($response);
}
/**
* Sendet eine API-Anfrage und gibt raw Response zurück
*/
public function sendRawRequest(
Method $method,
string $endpoint,
array $data = [],
array $queryParams = []
): ClientResponse {
$baseUrl = rtrim($this->config->baseUrl, '/');
$url = $baseUrl . '/' . ltrim($endpoint, '/');
$options = $this->defaultOptions;
if (! empty($queryParams)) {
$options = $options->with(['query' => $queryParams]);
}
if (in_array($method, [Method::GET, Method::DELETE]) && ! empty($data)) {
$options = $options->with(['query' => array_merge($options->query, $data)]);
$data = [];
}
$request = empty($data)
? new ClientRequest($method, $url, options: $options)
: ClientRequest::json($method, $url, $data, $options);
$response = $this->httpClient->send($request);
if (! $response->isSuccessful()) {
$this->throwApiException($response);
}
return $response;
}
/**
* Behandelt API-Response
*/
private function handleResponse(ClientResponse $response): array
{
if (! $response->isJson()) {
throw new ApiException(
'Expected JSON response, got: ' . $response->getContentType(),
0,
$response
);
}
try {
return $response->json();
} catch (\Exception $e) {
throw new ApiException(
'Invalid JSON response: ' . $e->getMessage(),
0,
$response
);
}
}
/**
* Wirft API-Exception
*/
private function throwApiException(ClientResponse $response): never
{
$data = [];
if ($response->isJson()) {
try {
$data = $response->json();
} catch (\Exception) {
// JSON parsing failed
}
}
$message = $this->formatErrorMessage($data, $response);
throw new ApiException($message, $response->status->value, $response);
}
/**
* Formatiert Fehlermeldung
*/
private function formatErrorMessage(array $responseData, ClientResponse $response): string
{
if (isset($responseData['message'])) {
return 'Netcup API Error: ' . $responseData['message'];
}
if (isset($responseData['error'])) {
return 'Netcup API Error: ' . $responseData['error'];
}
if (isset($responseData['shortMessage'])) {
$message = 'Netcup API Error: ' . $responseData['shortMessage'];
if (isset($responseData['longMessage'])) {
$message .= ' - ' . $responseData['longMessage'];
}
return $message;
}
return "Netcup API Error (HTTP {$response->status->value}): " .
substr($response->body, 0, 200);
}
/**
* Erstellt AuthConfig basierend auf NetcupConfig
*/
private function buildAuthConfig(): AuthConfig
{
return AuthConfig::custom([
'headers' => [
'X-API-KEY' => $this->config->apiKey,
'X-API-PASSWORD' => $this->config->apiPassword,
],
]);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\Api\Netcup;
use App\Framework\HttpClient\HttpClient;
final readonly class NetcupClient
{
public DnsService $dns;
public ServerService $servers;
public function __construct(
NetcupConfig $config,
HttpClient $httpClient
) {
$apiClient = new NetcupApiClient($config, $httpClient);
$this->dns = new DnsService($apiClient);
$this->servers = new ServerService($apiClient);
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\Api\Netcup;
use App\Framework\Config\Environment;
use App\Framework\Config\EnvKey;
use App\Framework\DI\Container;
use App\Framework\DI\Initializer;
use App\Framework\HttpClient\CurlHttpClient;
use App\Framework\HttpClient\HttpClient;
final readonly class NetcupClientInitializer
{
#[Initializer]
public function __invoke(Container $container): NetcupClient
{
$env = $container->get(Environment::class);
$httpClient = $container->get(HttpClient::class) ?? new CurlHttpClient();
$apiKey = $env->require(EnvKey::NETCUP_API_KEY);
$apiPassword = $env->require(EnvKey::NETCUP_API_PASSWORD);
$baseUrl = $env->get('NETCUP_BASE_URL', 'https://api.netcup.net');
$timeout = (float) $env->get('NETCUP_TIMEOUT', '30.0');
$config = new NetcupConfig(
apiKey: $apiKey,
apiPassword: $apiPassword,
baseUrl: $baseUrl,
timeout: $timeout
);
return new NetcupClient($config, $httpClient);
}
}

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\Api\Netcup;
final readonly class NetcupConfig
{
public function __construct(
public string $apiKey,
public string $apiPassword,
public string $baseUrl = 'https://api.netcup.net',
public float $timeout = 30.0
) {
}
}

View File

@@ -0,0 +1,387 @@
# Netcup API Client
## Übersicht
Dieser Client bietet eine strukturierte Schnittstelle für die Kommunikation mit der Netcup REST API. Er unterstützt DNS-Management-Operationen für Domains.
## Architektur
Der Client folgt dem Service-Layer-Pattern:
- **NetcupApiClient**: Low-level API Client für HTTP-Kommunikation
- **DnsService**: DNS-Record-Verwaltung mit Value Objects
- **ServerService**: Server-Management (VServer, Root Server)
- **Value Objects**: `DnsRecord` und `DnsRecordType` für type-safe DNS-Operationen
- **NetcupClient**: Facade, die alle Services bereitstellt
## Konfiguration
### Environment Variables
```env
NETCUP_API_KEY=your_api_key
NETCUP_API_PASSWORD=your_api_password
NETCUP_BASE_URL=https://api.netcup.net
NETCUP_TIMEOUT=30.0
```
### Manuelle Konfiguration
```php
use App\Infrastructure\Api\Netcup\NetcupClient;
use App\Infrastructure\Api\Netcup\NetcupConfig;
use App\Framework\HttpClient\CurlHttpClient;
$config = new NetcupConfig(
apiKey: 'your_api_key',
apiPassword: 'your_api_password',
baseUrl: 'https://api.netcup.net',
timeout: 30.0
);
$client = new NetcupClient($config, new CurlHttpClient());
```
### Dependency Injection
Der Client wird automatisch über den DI-Container bereitgestellt:
```php
use App\Infrastructure\Api\Netcup\NetcupClient;
// Im Controller oder Service
public function __construct(
private readonly NetcupClient $netcupClient
) {
}
```
## Verwendung
### DNS-Management mit Value Objects
#### DNS-Records auflisten
```php
use App\Infrastructure\Api\Netcup\ValueObjects\DnsRecord;
use App\Infrastructure\Api\Netcup\ValueObjects\DnsRecordType;
// Gibt Array von DnsRecord Value Objects zurück
$records = $netcupClient->dns->listRecords('example.com');
foreach ($records as $record) {
echo $record->name . ' -> ' . $record->content . PHP_EOL;
}
```
#### Einzelnen DNS-Record abrufen
```php
// Gibt DnsRecord Value Object zurück
$record = $netcupClient->dns->getRecord('example.com', 'record-id');
echo $record->type->value; // 'A', 'AAAA', 'CNAME', etc.
echo $record->content;
```
#### DNS-Record erstellen
```php
use App\Infrastructure\Api\Netcup\ValueObjects\DnsRecord;
use App\Infrastructure\Api\Netcup\ValueObjects\DnsRecordType;
// Mit Value Object (empfohlen)
$newRecord = new DnsRecord(
type: DnsRecordType::A,
name: 'www',
content: '192.0.2.1',
ttl: 3600
);
$created = $netcupClient->dns->createRecord('example.com', $newRecord);
```
#### DNS-Record mit Priority erstellen (MX, SRV)
```php
// MX Record mit Priority
$mxRecord = new DnsRecord(
type: DnsRecordType::MX,
name: '@',
content: 'mail.example.com',
ttl: 3600,
priority: 10
);
$created = $netcupClient->dns->createRecord('example.com', $mxRecord);
```
#### DNS-Record aktualisieren
```php
// Hole bestehenden Record
$record = $netcupClient->dns->getRecord('example.com', 'record-id');
// Erstelle aktualisierte Version
$updated = $record->withContent('192.0.2.2')->withTtl(7200);
// Aktualisiere
$result = $netcupClient->dns->updateRecord('example.com', 'record-id', $updated);
```
#### DNS-Record löschen
```php
$netcupClient->dns->deleteRecord('example.com', 'record-id');
```
### Server-Management
#### Server auflisten
```php
$servers = $netcupClient->servers->listServers();
```
#### Server-Informationen abrufen
```php
$server = $netcupClient->servers->getServer('server-id');
```
#### Server starten/stoppen/neustarten
```php
// Server starten
$netcupClient->servers->startServer('server-id');
// Server stoppen
$netcupClient->servers->stopServer('server-id');
// Server neu starten
$netcupClient->servers->restartServer('server-id');
```
#### Snapshot-Verwaltung
```php
// Snapshots auflisten
$snapshots = $netcupClient->servers->listSnapshots('server-id');
// Snapshot erstellen
$snapshot = $netcupClient->servers->createSnapshot('server-id', 'backup-2025-01-29');
// Snapshot wiederherstellen
$netcupClient->servers->restoreSnapshot('server-id', 'snapshot-id');
// Snapshot löschen
$netcupClient->servers->deleteSnapshot('server-id', 'snapshot-id');
```
## API-Referenz
### DnsService
#### `listRecords(string $domain): array<DnsRecord>`
Listet alle DNS-Records einer Domain auf.
**Parameter:**
- `$domain`: Die Domain (z.B. 'example.com')
**Rückgabe:** Array von `DnsRecord` Value Objects
#### `getRecord(string $domain, string $recordId): DnsRecord`
Ruft einen einzelnen DNS-Record ab.
**Parameter:**
- `$domain`: Die Domain
- `$recordId`: Die Record-ID
**Rückgabe:** `DnsRecord` Value Object
#### `createRecord(string $domain, DnsRecord $record): DnsRecord`
Erstellt einen neuen DNS-Record.
**Parameter:**
- `$domain`: Die Domain
- `$record`: `DnsRecord` Value Object mit Record-Daten
**Rückgabe:** `DnsRecord` Value Object des erstellten Records
#### `updateRecord(string $domain, string $recordId, DnsRecord $record): DnsRecord`
Aktualisiert einen DNS-Record.
**Parameter:**
- `$domain`: Die Domain
- `$recordId`: Die Record-ID
- `$record`: `DnsRecord` Value Object mit aktualisierten Daten
**Rückgabe:** `DnsRecord` Value Object des aktualisierten Records
#### `deleteRecord(string $domain, string $recordId): void`
Löscht einen DNS-Record.
**Parameter:**
- `$domain`: Die Domain
- `$recordId`: Die Record-ID
### DnsRecord Value Object
#### Properties
- `DnsRecordType $type`: Der DNS-Record-Typ (A, AAAA, CNAME, MX, etc.)
- `string $name`: Der Hostname/Name des Records
- `string $content`: Der Inhalt des Records (IP-Adresse, Domain, etc.)
- `int $ttl`: Time To Live in Sekunden
- `?int $priority`: Priorität (für MX und SRV Records)
- `?string $id`: Record-ID (wenn aus API abgerufen)
#### Factory-Methoden
```php
// Aus Array erstellen (API-Response)
$record = DnsRecord::fromArray($apiResponse);
// Direkt erstellen
$record = new DnsRecord(
type: DnsRecordType::A,
name: 'www',
content: '192.0.2.1',
ttl: 3600
);
```
#### Immutable Transformation
```php
// Neuen Record mit aktualisiertem Content erstellen
$updated = $record->withContent('192.0.2.2');
// Neuen Record mit aktualisiertem TTL erstellen
$updated = $record->withTtl(7200);
// Kombiniert
$updated = $record->withContent('192.0.2.2')->withTtl(7200);
```
### DnsRecordType Enum
Unterstützte DNS-Record-Typen: `A`, `AAAA`, `CNAME`, `MX`, `TXT`, `NS`, `SRV`, `PTR`, `SOA`, `CAA`
#### Methoden
```php
// Prüfen ob Priority erforderlich ist
$type->requiresPriority(); // true für MX, SRV
// Prüfen ob es ein IP-Record ist
$type->isIpRecord(); // true für A, AAAA
```
### ServerService
#### `listServers(): array`
Listet alle Server auf.
**Rückgabe:** Array mit Server-Informationen
#### `getServer(string $serverId): array`
Ruft Server-Informationen ab.
**Parameter:**
- `$serverId`: Die Server-ID
**Rückgabe:** Array mit Server-Daten
#### `startServer(string $serverId): void`
Startet einen Server.
**Parameter:**
- `$serverId`: Die Server-ID
#### `stopServer(string $serverId): void`
Stoppt einen Server.
**Parameter:**
- `$serverId`: Die Server-ID
#### `restartServer(string $serverId): void`
Startet einen Server neu.
**Parameter:**
- `$serverId`: Die Server-ID
#### `listSnapshots(string $serverId): array`
Listet alle Snapshots eines Servers auf.
**Parameter:**
- `$serverId`: Die Server-ID
**Rückgabe:** Array mit Snapshot-Informationen
#### `createSnapshot(string $serverId, string $name): array`
Erstellt einen Snapshot.
**Parameter:**
- `$serverId`: Die Server-ID
- `$name`: Der Name des Snapshots
**Rückgabe:** Array mit Snapshot-Daten
#### `deleteSnapshot(string $serverId, string $snapshotId): void`
Löscht einen Snapshot.
**Parameter:**
- `$serverId`: Die Server-ID
- `$snapshotId`: Die Snapshot-ID
#### `restoreSnapshot(string $serverId, string $snapshotId): void`
Stellt einen Snapshot wieder her.
**Parameter:**
- `$serverId`: Die Server-ID
- `$snapshotId`: Die Snapshot-ID
## Fehlerbehandlung
Alle API-Fehler werden als `ApiException` geworfen:
```php
use App\Framework\Api\ApiException;
use App\Infrastructure\Api\Netcup\ValueObjects\DnsRecord;
use App\Infrastructure\Api\Netcup\ValueObjects\DnsRecordType;
try {
$record = new DnsRecord(
type: DnsRecordType::A,
name: 'www',
content: '192.0.2.1',
ttl: 3600
);
$created = $netcupClient->dns->createRecord('example.com', $record);
} catch (ApiException $e) {
// Fehlerbehandlung
echo $e->getMessage();
}
```
## Authentifizierung
Die Authentifizierung erfolgt über API-Key und API-Passwort, die als Custom-Header (`X-API-KEY` und `X-API-PASSWORD`) mit jeder Anfrage gesendet werden.
Die Credentials können im Netcup Customer Control Panel (CCP) unter "Stammdaten" -> "API" generiert werden.

View File

@@ -0,0 +1,145 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\Api\Netcup;
use App\Framework\Http\Method;
final readonly class ServerService
{
public function __construct(
private NetcupApiClient $apiClient
) {
}
/**
* Listet alle Server auf
*
* @return array Liste der Server
*/
public function listServers(): array
{
return $this->apiClient->request(
Method::GET,
'servers'
);
}
/**
* Ruft Server-Informationen ab
*
* @param string $serverId Die Server-ID
* @return array Server-Daten
*/
public function getServer(string $serverId): array
{
return $this->apiClient->request(
Method::GET,
"servers/{$serverId}"
);
}
/**
* Startet einen Server
*
* @param string $serverId Die Server-ID
* @return void
*/
public function startServer(string $serverId): void
{
$this->apiClient->sendRawRequest(
Method::POST,
"servers/{$serverId}/start"
);
}
/**
* Stoppt einen Server
*
* @param string $serverId Die Server-ID
* @return void
*/
public function stopServer(string $serverId): void
{
$this->apiClient->sendRawRequest(
Method::POST,
"servers/{$serverId}/stop"
);
}
/**
* Startet einen Server neu
*
* @param string $serverId Die Server-ID
* @return void
*/
public function restartServer(string $serverId): void
{
$this->apiClient->sendRawRequest(
Method::POST,
"servers/{$serverId}/restart"
);
}
/**
* Listet alle Snapshots eines Servers auf
*
* @param string $serverId Die Server-ID
* @return array Liste der Snapshots
*/
public function listSnapshots(string $serverId): array
{
return $this->apiClient->request(
Method::GET,
"servers/{$serverId}/snapshots"
);
}
/**
* Erstellt einen Snapshot
*
* @param string $serverId Die Server-ID
* @param string $name Der Name des Snapshots
* @return array Erstellter Snapshot
*/
public function createSnapshot(string $serverId, string $name): array
{
return $this->apiClient->request(
Method::POST,
"servers/{$serverId}/snapshots",
['name' => $name]
);
}
/**
* Löscht einen Snapshot
*
* @param string $serverId Die Server-ID
* @param string $snapshotId Die Snapshot-ID
* @return void
*/
public function deleteSnapshot(string $serverId, string $snapshotId): void
{
$this->apiClient->sendRawRequest(
Method::DELETE,
"servers/{$serverId}/snapshots/{$snapshotId}"
);
}
/**
* Stellt einen Snapshot wieder her
*
* @param string $serverId Die Server-ID
* @param string $snapshotId Die Snapshot-ID
* @return void
*/
public function restoreSnapshot(string $serverId, string $snapshotId): void
{
$this->apiClient->sendRawRequest(
Method::POST,
"servers/{$serverId}/snapshots/{$snapshotId}/restore"
);
}
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\Api\Netcup\ValueObjects;
use InvalidArgumentException;
/**
* DNS Record Value Object
*
* Immutable value object representing a DNS record with type, name, content, TTL, and optional priority.
*/
final readonly class DnsRecord
{
public function __construct(
public DnsRecordType $type,
public string $name,
public string $content,
public int $ttl,
public ?int $priority = null,
public ?string $id = null
) {
if (empty($this->name)) {
throw new InvalidArgumentException('DNS record name cannot be empty');
}
if (empty($this->content)) {
throw new InvalidArgumentException('DNS record content cannot be empty');
}
if ($this->ttl < 0) {
throw new InvalidArgumentException('DNS record TTL must be non-negative');
}
if ($this->type->requiresPriority() && $this->priority === null) {
throw new InvalidArgumentException(
"DNS record type {$this->type->value} requires a priority value"
);
}
if (! $this->type->requiresPriority() && $this->priority !== null) {
throw new InvalidArgumentException(
"DNS record type {$this->type->value} does not support priority"
);
}
if ($this->priority !== null && $this->priority < 0) {
throw new InvalidArgumentException('DNS record priority must be non-negative');
}
}
/**
* Create DnsRecord from array (API response or input data)
*/
public static function fromArray(array $data): self
{
$type = DnsRecordType::tryFrom($data['type'] ?? $data['recordtype'] ?? '')
?? throw new InvalidArgumentException("Invalid DNS record type: " . ($data['type'] ?? $data['recordtype'] ?? 'unknown'));
return new self(
type: $type,
name: $data['name'] ?? $data['hostname'] ?? '',
content: $data['content'] ?? $data['destination'] ?? $data['value'] ?? '',
ttl: isset($data['ttl']) ? (int) $data['ttl'] : 3600,
priority: isset($data['priority']) ? (int) $data['priority'] : null,
id: $data['id'] ?? $data['recordid'] ?? null
);
}
/**
* Convert to array for API requests
*/
public function toArray(): array
{
$data = [
'type' => $this->type->value,
'name' => $this->name,
'content' => $this->content,
'ttl' => $this->ttl,
];
if ($this->priority !== null) {
$data['priority'] = $this->priority;
}
if ($this->id !== null) {
$data['id'] = $this->id;
}
return $data;
}
/**
* Create a new DnsRecord with updated content
*/
public function withContent(string $content): self
{
return new self(
type: $this->type,
name: $this->name,
content: $content,
ttl: $this->ttl,
priority: $this->priority,
id: $this->id
);
}
/**
* Create a new DnsRecord with updated TTL
*/
public function withTtl(int $ttl): self
{
return new self(
type: $this->type,
name: $this->name,
content: $this->content,
ttl: $ttl,
priority: $this->priority,
id: $this->id
);
}
/**
* Check if this record has an ID (is persisted)
*/
public function hasId(): bool
{
return $this->id !== null;
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\Api\Netcup\ValueObjects;
/**
* DNS Record Type
*
* Represents the type of a DNS record (A, AAAA, CNAME, MX, etc.)
*/
enum DnsRecordType: string
{
case A = 'A';
case AAAA = 'AAAA';
case CNAME = 'CNAME';
case MX = 'MX';
case TXT = 'TXT';
case NS = 'NS';
case SRV = 'SRV';
case PTR = 'PTR';
case SOA = 'SOA';
case CAA = 'CAA';
/**
* Check if this record type requires a priority value
*/
public function requiresPriority(): bool
{
return match ($this) {
self::MX, self::SRV => true,
default => false,
};
}
/**
* Check if this record type is for IP addresses
*/
public function isIpRecord(): bool
{
return match ($this) {
self::A, self::AAAA => true,
default => false,
};
}
}

View File

@@ -5,12 +5,14 @@ declare(strict_types=1);
namespace App\Infrastructure\Api\RapidMail\ApiRequests;
use App\Framework\ApiGateway\ApiRequest;
use App\Framework\ApiGateway\HasAuth;
use App\Framework\ApiGateway\HasPayload;
use App\Framework\ApiGateway\ValueObjects\ApiEndpoint;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Http\Headers;
use App\Framework\Http\Method as HttpMethod;
use App\Framework\Http\Url\Url;
use App\Framework\HttpClient\AuthConfig;
use App\Framework\Retry\ExponentialBackoffStrategy;
use App\Framework\Retry\RetryStrategy;
use App\Infrastructure\Api\RapidMail\RapidMailConfig;
@@ -31,7 +33,7 @@ use App\Infrastructure\Api\RapidMail\RapidMailConfig;
*
* $response = $apiGateway->send($request);
*/
final readonly class CreateRecipientApiRequest implements ApiRequest, HasPayload
final readonly class CreateRecipientApiRequest implements ApiRequest, HasPayload, HasAuth
{
public function __construct(
private RapidMailConfig $config,
@@ -71,13 +73,14 @@ final readonly class CreateRecipientApiRequest implements ApiRequest, HasPayload
);
}
public function getAuth(): AuthConfig
{
return AuthConfig::basic($this->config->username, $this->config->password);
}
public function getHeaders(): Headers
{
// Basic Auth
$credentials = base64_encode("{$this->config->username}:{$this->config->password}");
return new Headers([
'Authorization' => "Basic {$credentials}",
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]);

View File

@@ -5,11 +5,13 @@ declare(strict_types=1);
namespace App\Infrastructure\Api\RapidMail\ApiRequests;
use App\Framework\ApiGateway\ApiRequest;
use App\Framework\ApiGateway\HasAuth;
use App\Framework\ApiGateway\ValueObjects\ApiEndpoint;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Http\Headers;
use App\Framework\Http\Method as HttpMethod;
use App\Framework\Http\Url\Url;
use App\Framework\HttpClient\AuthConfig;
use App\Framework\Retry\ExponentialBackoffStrategy;
use App\Framework\Retry\RetryStrategy;
use App\Infrastructure\Api\RapidMail\RapidMailConfig;
@@ -26,7 +28,7 @@ use App\Infrastructure\Api\RapidMail\RapidMailConfig;
*
* $response = $apiGateway->send($request);
*/
final readonly class DeleteRecipientApiRequest implements ApiRequest
final readonly class DeleteRecipientApiRequest implements ApiRequest, HasAuth
{
public function __construct(
private RapidMailConfig $config,
@@ -61,13 +63,14 @@ final readonly class DeleteRecipientApiRequest implements ApiRequest
);
}
public function getAuth(): AuthConfig
{
return AuthConfig::basic($this->config->username, $this->config->password);
}
public function getHeaders(): Headers
{
// Basic Auth
$credentials = base64_encode("{$this->config->username}:{$this->config->password}");
return new Headers([
'Authorization' => "Basic {$credentials}",
'Accept' => 'application/json',
]);
}

View File

@@ -5,11 +5,13 @@ declare(strict_types=1);
namespace App\Infrastructure\Api\RapidMail\ApiRequests;
use App\Framework\ApiGateway\ApiRequest;
use App\Framework\ApiGateway\HasAuth;
use App\Framework\ApiGateway\ValueObjects\ApiEndpoint;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Http\Headers;
use App\Framework\Http\Method as HttpMethod;
use App\Framework\Http\Url\Url;
use App\Framework\HttpClient\AuthConfig;
use App\Framework\Retry\ExponentialBackoffStrategy;
use App\Framework\Retry\RetryStrategy;
use App\Infrastructure\Api\RapidMail\RapidMailConfig;
@@ -26,7 +28,7 @@ use App\Infrastructure\Api\RapidMail\RapidMailConfig;
*
* $response = $apiGateway->send($request);
*/
final readonly class GetRecipientApiRequest implements ApiRequest
final readonly class GetRecipientApiRequest implements ApiRequest, HasAuth
{
public function __construct(
private RapidMailConfig $config,
@@ -61,13 +63,14 @@ final readonly class GetRecipientApiRequest implements ApiRequest
);
}
public function getAuth(): AuthConfig
{
return AuthConfig::basic($this->config->username, $this->config->password);
}
public function getHeaders(): Headers
{
// Basic Auth
$credentials = base64_encode("{$this->config->username}:{$this->config->password}");
return new Headers([
'Authorization' => "Basic {$credentials}",
'Accept' => 'application/json',
]);
}

View File

@@ -5,11 +5,13 @@ declare(strict_types=1);
namespace App\Infrastructure\Api\RapidMail\ApiRequests;
use App\Framework\ApiGateway\ApiRequest;
use App\Framework\ApiGateway\HasAuth;
use App\Framework\ApiGateway\ValueObjects\ApiEndpoint;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Http\Headers;
use App\Framework\Http\Method as HttpMethod;
use App\Framework\Http\Url\Url;
use App\Framework\HttpClient\AuthConfig;
use App\Framework\Retry\ExponentialBackoffStrategy;
use App\Framework\Retry\RetryStrategy;
use App\Infrastructure\Api\RapidMail\RapidMailConfig;
@@ -28,7 +30,7 @@ use App\Infrastructure\Api\RapidMail\RapidMailConfig;
*
* $response = $apiGateway->send($request);
*/
final readonly class SearchRecipientsApiRequest implements ApiRequest
final readonly class SearchRecipientsApiRequest implements ApiRequest, HasAuth
{
public function __construct(
private RapidMailConfig $config,
@@ -84,13 +86,14 @@ final readonly class SearchRecipientsApiRequest implements ApiRequest
);
}
public function getAuth(): AuthConfig
{
return AuthConfig::basic($this->config->username, $this->config->password);
}
public function getHeaders(): Headers
{
// Basic Auth
$credentials = base64_encode("{$this->config->username}:{$this->config->password}");
return new Headers([
'Authorization' => "Basic {$credentials}",
'Accept' => 'application/json',
]);
}

View File

@@ -5,12 +5,14 @@ declare(strict_types=1);
namespace App\Infrastructure\Api\RapidMail\ApiRequests;
use App\Framework\ApiGateway\ApiRequest;
use App\Framework\ApiGateway\HasAuth;
use App\Framework\ApiGateway\HasPayload;
use App\Framework\ApiGateway\ValueObjects\ApiEndpoint;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Http\Headers;
use App\Framework\Http\Method as HttpMethod;
use App\Framework\Http\Url\Url;
use App\Framework\HttpClient\AuthConfig;
use App\Framework\Retry\ExponentialBackoffStrategy;
use App\Framework\Retry\RetryStrategy;
use App\Infrastructure\Api\RapidMail\RapidMailConfig;
@@ -31,7 +33,7 @@ use App\Infrastructure\Api\RapidMail\RapidMailConfig;
*
* $response = $apiGateway->send($request);
*/
final readonly class UpdateRecipientApiRequest implements ApiRequest, HasPayload
final readonly class UpdateRecipientApiRequest implements ApiRequest, HasPayload, HasAuth
{
public function __construct(
private RapidMailConfig $config,
@@ -72,13 +74,14 @@ final readonly class UpdateRecipientApiRequest implements ApiRequest, HasPayload
);
}
public function getAuth(): AuthConfig
{
return AuthConfig::basic($this->config->username, $this->config->password);
}
public function getHeaders(): Headers
{
// Basic Auth
$credentials = base64_encode("{$this->config->username}:{$this->config->password}");
return new Headers([
'Authorization' => "Basic {$credentials}",
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]);

View File

@@ -5,11 +5,14 @@ declare(strict_types=1);
namespace App\Infrastructure\Api\Shopify;
use App\Framework\ApiGateway\ApiRequest;
use App\Framework\ApiGateway\HasAuth;
use App\Framework\ApiGateway\HasPayload;
use App\Framework\ApiGateway\ValueObjects\ApiEndpoint;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Http\Headers;
use App\Framework\Http\Method as HttpMethod;
use App\Framework\Http\Url\Url;
use App\Framework\HttpClient\AuthConfig;
use App\Framework\Retry\RetryStrategy;
use App\Infrastructure\Api\Shopify\ValueObjects\{ShopifyApiKey, ShopifyStore};
@@ -31,7 +34,7 @@ use App\Infrastructure\Api\Shopify\ValueObjects\{ShopifyApiKey, ShopifyStore};
*
* $response = $apiGateway->send($request);
*/
final readonly class CreateOrderApiRequest implements ApiRequest
final readonly class CreateOrderApiRequest implements ApiRequest, HasPayload, HasAuth
{
public function __construct(
private ShopifyApiKey $apiKey,
@@ -73,10 +76,16 @@ final readonly class CreateOrderApiRequest implements ApiRequest
return $this->retryStrategy;
}
public function getAuth(): AuthConfig
{
return AuthConfig::custom([
'X-Shopify-Access-Token' => $this->apiKey->value,
]);
}
public function getHeaders(): Headers
{
return new Headers([
'X-Shopify-Access-Token' => $this->apiKey->value,
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]);