fix: align directadmin vacation api payload

This commit is contained in:
Marek Miklewicz
2026-06-02 23:10:01 +02:00
parent 86eb1dddbd
commit 27d95e1baf
3 changed files with 158 additions and 13 deletions
+97 -10
View File
@@ -5,12 +5,15 @@ class DirectAdminVacationApi
{
/** @var callable|null */
private $request;
/** @var callable|null */
private $runner;
private string $daBinary = '/usr/local/directadmin/directadmin';
public function __construct(string $daBinary = '/usr/local/directadmin/directadmin', ?callable $request = null)
public function __construct(string $daBinary = '/usr/local/directadmin/directadmin', ?callable $request = null, ?callable $runner = null)
{
$this->daBinary = $daBinary;
$this->request = $request;
$this->runner = $runner;
}
public function directAdminBinary(): string
@@ -30,17 +33,15 @@ class DirectAdminVacationApi
[$startYear, $startMonth, $startDay] = explode('-', $startDate);
[$endYear, $endMonth, $endDay] = explode('-', $endDate);
$subjectPrefix = (string)($rule['subject_prefix'] ?? $rule['subject'] ?? '');
$replyFrequency = !empty($rule['reply_every_message']) ? '0' : (string)max(0, (int)($rule['repeat_minutes'] ?? 1440));
return [
$payload = [
'action' => $exists ? 'modify' : 'create',
'domain' => $domain,
'user' => $local,
'email' => $email,
'text' => (string)($rule['body'] ?? ''),
'subject_prefix' => $subjectPrefix,
'subject' => $subjectPrefix,
'reply_frequency' => $replyFrequency,
'reply_once_time' => $replyFrequency,
'reply_encoding' => 'UTF-8',
'reply_content_type' => 'text/plain',
'reply_once_time' => $this->replyOnceTime($rule),
'starttime' => $startTime,
'startyear' => $startYear,
'startmonth' => $startMonth,
@@ -49,8 +50,11 @@ class DirectAdminVacationApi
'endyear' => $endYear,
'endmonth' => $endMonth,
'endday' => $endDay,
'create' => $exists ? '' : 'Create',
];
if (!$exists) {
$payload['create'] = 'Create';
}
return $payload;
}
/** @param array<string,mixed> $rule */
@@ -127,13 +131,92 @@ class DirectAdminVacationApi
}
$baseUrl = $this->apiUrl($owner);
$url = rtrim($baseUrl, '/') . '/' . $endpoint;
$args = ['curl', '-fsS', '-X', 'POST'];
$args = ['curl', '-sS', '-X', 'POST', '-w', "\n%{http_code}"];
foreach ($payload as $key => $value) {
$args[] = '--data-urlencode';
$args[] = $key . '=' . $value;
}
$args[] = $url;
return $this->run($args, '');
return $this->parseHttpResponse($this->run($args, ''));
}
/** @param array<string,mixed> $rule */
private function replyOnceTime(array $rule): string
{
if (!empty($rule['reply_every_message'])) {
return '0';
}
return match ((int)($rule['repeat_minutes'] ?? 1440)) {
30 => '30m',
60 => '1h',
120 => '2h',
360 => '6h',
720 => '12h',
1440 => '1d',
2880 => '2d',
10080 => '1w',
default => '1d',
};
}
private function parseHttpResponse(string $output): string
{
if (!preg_match("/\n([0-9]{3})$/", $output, $m, PREG_OFFSET_CAPTURE)) {
return $output;
}
$code = (int)$m[1][0];
$body = substr($output, 0, (int)$m[0][1]);
if ($code < 200 || $code >= 400) {
throw new RuntimeException('DirectAdmin API HTTP ' . $code . ': ' . $this->compactBody($body));
}
$this->throwIfApiError($body);
return $body;
}
private function throwIfApiError(string $body): void
{
$trimmed = trim($body);
if ($trimmed === '') {
return;
}
$json = json_decode($trimmed, true);
if (is_array($json) && isset($json['error']) && $this->truthyError($json['error'])) {
throw new RuntimeException('DirectAdmin API error: ' . $this->compactBody(implode(' ', array_filter([
(string)($json['text'] ?? ''),
(string)($json['details'] ?? ''),
(string)($json['result'] ?? ''),
(string)($json['error'] ?? ''),
]))));
}
$fields = [];
parse_str($trimmed, $fields);
if (isset($fields['error']) && $this->truthyError($fields['error'])) {
throw new RuntimeException('DirectAdmin API error: ' . $this->compactBody(implode(' ', array_filter([
is_scalar($fields['text'] ?? null) ? (string)$fields['text'] : '',
is_scalar($fields['details'] ?? null) ? (string)$fields['details'] : '',
is_scalar($fields['result'] ?? null) ? (string)$fields['result'] : '',
is_scalar($fields['error'] ?? null) ? (string)$fields['error'] : '',
]))));
}
}
private function truthyError(mixed $value): bool
{
return in_array(strtolower(trim((string)$value)), ['1', 'yes', 'true', 'error'], true);
}
private function compactBody(string $body): string
{
$body = trim(preg_replace('/[[:cntrl:]]+/', ' ', $body) ?? $body);
if ($body === '') {
return 'empty response body';
}
if (strlen($body) > 1200) {
return substr($body, 0, 1200) . '...';
}
return $body;
}
private function apiUrl(string $owner): string
@@ -164,6 +247,10 @@ class DirectAdminVacationApi
/** @param string[] $args */
private function run(array $args, string $stdin): string
{
if ($this->runner !== null) {
$result = call_user_func($this->runner, $args, $stdin);
return (string)$result;
}
if (($args[0] ?? '') !== '' && str_contains($args[0], '/') && !is_executable($args[0])) {
throw new RuntimeException('DirectAdmin API command is not executable: ' . $args[0]);
}