Files
thetool/application/Radius/RadiusController.php
2025-12-03 14:08:44 +00:00

623 lines
59 KiB
PHP

<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
class RadiusController extends mfBaseController {
private User $me;
protected function init(): void {
$this->needlogin=true;
$me = new User();
$me->loadMe();
$this->layout()->set("me", $me);
$this->me = $me;
if (!$this->me->is("Admin")) $this->redirect("Dashboard");
}
protected function indexAction() {
$this->layout()->set('additionalJS', ["plugins/chart.js/chart.4.4.6.js", "plugins/chart.js/chartjs-adapter-moment.min.js"]);
$allowedAcsUserIds = [9, 13, 25, 65, 135, 145, 178];
$acsEnabled = in_array($this->me->id, $allowedAcsUserIds);
Helper::renderVue($this, $this->mod, "Radius", [
'CAN_BILLING' => $this->me->can("Billing"),
'HIDE_PAGE_TITLE' => true,
'USER_ID' => $this->me->id,
'ACS_ENABLED' => $acsEnabled
]);
}
protected function proxyUnsecureHTTPRequestToRadiusAction() {
$url = "http://radius.xinon.at/api.php?" . http_build_query($_GET);
$url = str_replace("proxyUnsecureHTTPRequestToRadius", "", $url);
$opts = [
"http" => [
"method" => "GET",
"header" => "Authorization: Basic " . base64_encode("admin:saveman"),
]
];
header("Content-Type: application/json");
$context = stream_context_create($opts);
$response = file_get_contents($url, false, $context);
echo $response;
die();
}
/**
* Run speedtest via ACS proxy
*/
protected function genieacsRunSpeedtestAction() {
try {
$input = json_decode(file_get_contents('php://input'), true);
$ip = $input['ip'] ?? null;
if (!$ip) {
self::sendError("IP address is required");
}
$url = "http://acs.xinon.at:5000/run-speedtest";
$apiKey = "2H9zWrgxPEJL9MZ1yTGtWh16cPCu0AsQ";
$data = json_encode(['ip' => $ip]);
$opts = [
"http" => [
"method" => "POST",
"header" => "Content-Type: application/json\r\n" .
"X-API-Key: " . $apiKey . "\r\n" .
"Content-Length: " . strlen($data) . "\r\n",
"content" => $data
]
];
$context = stream_context_create($opts);
$response = file_get_contents($url, false, $context);
if ($response === false) {
self::sendError("Failed to connect to speedtest server");
}
header("Content-Type: application/json");
echo $response;
die();
} catch (Exception $e) {
error_log("GenieACS runSpeedtest error: " . $e->getMessage());
self::sendError("Error running speedtest: " . $e->getMessage());
}
}
protected function sendCustomerEmailAction() {
$input = json_decode(file_get_contents('php://input'), true);
if (!$input || !isset($input['username'], $input['year'], $input['month'], $input['monthlySummary'], $input['monthlyDetails'], $input['recipient']))
self::sendError("Ungültige oder unvollständige Eingabedaten.");
$username = $input['username'];
$year = $input['year'];
$month = $input['month'];
$monthlySummary = $input['monthlySummary'];
$monthlyDetails = $input['monthlyDetails'];
$chartImage = $input['chartImage'] ?? '';
$recipient = $input['recipient'];
if (!filter_var($recipient, FILTER_VALIDATE_EMAIL))
self::sendError("Ungültige E-Mail-Adresse des Empfängers.");
$monthNames = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'];
$monthName = $monthNames[$month - 1] ?? $month;
$subject = "Ihre Transfer-Statistik für {$monthName} {$year}";
// --- Helper Functions ---
$formatBytes = function($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
};
$formatDuration = function($seconds) {
$seconds = max(0, intval($seconds));
if ($seconds === 0) return '00:00:00';
$days = floor($seconds / 86400);
if ($days > 0) {
$remainder = $seconds % 86400;
$dayString = $days . ' ' . ($days == 1 ? 'Tag' : 'Tage');
if ($remainder === 0) return $dayString;
return $dayString . ', ' . sprintf('%02d:%02d:%02d', floor($remainder / 3600), floor(($remainder % 3600) / 60), $remainder % 60);
}
return sprintf('%02d:%02d:%02d', floor($seconds / 3600), floor(($seconds % 3600) / 60), $seconds % 60);
};
// --- Data Preparation ---
$customerNumber = preg_replace('/[^0-9]/', '', $username) ?: 'Kunde';
$logoToolPath = LIBDIR . '/../public/assets/images/the-tool-logo.png';
$logoXinonPath = LIBDIR . '/../public/assets/images/xinon-full.png';
$logoToolTag = file_exists($logoToolPath) ? '<img src="cid:logo_thetool" alt="TheTOOL Logo" width="140" style="display:block; border:0; width:140px; height:auto;">' : '';
$logoXinonTag = file_exists($logoXinonPath) ? '<img src="cid:logo_xinon" alt="XINON Logo" width="140" style="display:block; border:0; width:140px; height:auto;">' : '';
$currentYear = date("Y");
$xinonBlue = '#005384';
// Monthly summary values
$monthlyTotal = $formatBytes($monthlySummary['grandTotalBytes']);
$monthlyDuration = $formatDuration($monthlySummary['totalDurationSeconds']);
$monthlyUpload = $formatBytes($monthlySummary['totalUploadBytes']);
$monthlyDownload = $formatBytes($monthlySummary['totalDownloadBytes']);
// --- Daily Details Table Generation ---
$dailyDetailsTable = '';
foreach ($monthlyDetails as $detail) {
$date = date("d.m.Y", strtotime($detail['startTime']));
$dailyDetailsTable .= "
<tr>
<td style=\"padding: 8px 12px; border-bottom: 1px solid #eeeeee; font-size:14px; color:#333333;\">{$date}</td>
<td style=\"padding: 8px 12px; border-bottom: 1px solid #eeeeee; text-align: right; font-size:14px; color:#333333;\">{$formatDuration($detail['durationSeconds'])}</td>
<td style=\"padding: 8px 12px; border-bottom: 1px solid #eeeeee; text-align: right; font-size:14px; color:#333333;\">{$formatBytes($detail['uploadBytes'])}</td>
<td style=\"padding: 8px 12px; border-bottom: 1px solid #eeeeee; text-align: right; font-size:14px; color:#333333;\">{$formatBytes($detail['downloadBytes'])}</td>
<td style=\"padding: 8px 12px; border-bottom: 1px solid #eeeeee; text-align: right; font-size:14px; color:#333333;\"><strong>{$formatBytes($detail['totalBytes'])}</strong></td>
</tr>";
}
// --- Base64 Encoded Icons (shortened as requested) ---
$icons = [
'total' => 'data:image/png;base64,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',
'download' => 'data:image/png;base64,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',
'upload' => 'data:image/png;base64,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',
'duration' => 'data:image/png;base64,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',
];
// --- Bulletproof HTML Email Template ---
$html = <<<HTML
<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{$subject}</title>
<style type="text/css">
@media screen and (max-width: 600px) {
.column, .column-half {
width: 100% !important;
display: block !important;
padding-right: 0 !important;
padding-left: 0 !important;
padding-bottom: 16px !important;
}
.column-half:last-child {
padding-bottom: 0 !important;
}
.main-padding {
padding: 15px !important;
}
}
</style>
</head>
<body style="margin: 0; padding: 0; background-color: #f3f4f6; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f4f6;">
<tr>
<td align="center" style="padding: 20px 10px;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; border-top: 5px solid {$xinonBlue};">
<tr>
<td style="padding: 25px 20px; border-bottom: 1px solid #eeeeee;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td align="left">{$logoToolTag}</td>
<td align="right">{$logoXinonTag}</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="main-padding" style="padding: 30px 40px 35px 40px; font-family: Arial, sans-serif;">
<h1 style="font-family: Arial, sans-serif; font-size: 28px; font-weight: bold; color: #111827; margin: 0 0 16px 0;">Ihre Transfer-Statistik</h1>
<p style="font-family: Arial, sans-serif; font-size: 18px; color: #4b5563; margin: 0 0 24px;">für <strong>{$monthName} {$year}</strong></p>
<p style="font-family: Arial, sans-serif; font-size: 16px; color: #4b5563; margin: 0 0 30px; line-height: 1.5;">Sehr geehrter Kunde,<br>anbei finden Sie eine Übersicht Ihrer Netzwerk-Nutzung.</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td class="column-half" width="50%" align="center" valign="top" style="padding-right: 8px;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px;">
<tr><td align="center" style="padding: 16px 16px 8px 16px;"><img src="{$icons['total']}" alt="Gesamt" width="28" height="28" style="display:block; border:0;"></td></tr>
<tr><td align="center" style="font-family: Arial, sans-serif; font-size: 12px; color: #64748b; padding-bottom: 4px;">Monat gesamt</td></tr>
<tr><td align="center" style="font-family: monospace, sans-serif; font-size: 18px; font-weight: bold; color: #1e293b; padding: 0 16px 16px 16px;">{$monthlyTotal}</td></tr>
</table>
</td>
<td class="column-half" width="50%" align="center" valign="top" style="padding-left: 8px;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px;">
<tr><td align="center" style="padding: 16px 16px 8px 16px;"><img src="{$icons['download']}" alt="Download" width="28" height="28" style="display:block; border:0;"></td></tr>
<tr><td align="center" style="font-family: Arial, sans-serif; font-size: 12px; color: #64748b; padding-bottom: 4px;">Download</td></tr>
<tr><td align="center" style="font-family: monospace, sans-serif; font-size: 18px; font-weight: bold; color: #1e293b; padding: 0 16px 16px 16px;">{$monthlyDownload}</td></tr>
</table>
</td>
</tr>
<tr><td height="16" style="font-size:1px; line-height:1px;">&nbsp;</td></tr>
<tr>
<td class="column-half" width="50%" align="center" valign="top" style="padding-right: 8px;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px;">
<tr><td align="center" style="padding: 16px 16px 8px 16px;"><img src="{$icons['upload']}" alt="Upload" width="28" height="28" style="display:block; border:0;"></td></tr>
<tr><td align="center" style="font-family: Arial, sans-serif; font-size: 12px; color: #64748b; padding-bottom: 4px;">Upload</td></tr>
<tr><td align="center" style="font-family: monospace, sans-serif; font-size: 18px; font-weight: bold; color: #1e293b; padding: 0 16px 16px 16px;">{$monthlyUpload}</td></tr>
</table>
</td>
<td class="column-half" width="50%" align="center" valign="top" style="padding-left: 8px;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px;">
<tr><td align="center" style="padding: 16px 16px 8px 16px;"><img src="{$icons['duration']}" alt="Dauer" width="28" height="28" style="display:block; border:0;"></td></tr>
<tr><td align="center" style="font-family: Arial, sans-serif; font-size: 12px; color: #64748b; padding-bottom: 4px;">Dauer</td></tr>
<tr><td align="center" style="font-family: monospace, sans-serif; font-size: 18px; font-weight: bold; color: #1e293b; padding: 0 16px 16px 16px;">{$monthlyDuration}</td></tr>
</table>
</td>
</tr>
</table>
<div style="text-align: center; margin: 30px 0;"><img src="cid:chart_image" alt="Transfer Statistik Chart" width="600" style="max-width: 100%; width: 100%; height: auto; display: block; border: 1px solid #e5e7eb; border-radius: 8px;"></div>
<h2 style="font-family: Arial, sans-serif; font-size: 20px; font-weight: bold; color: #111827; margin: 30px 0 16px 0;">Tagesübersicht</h2>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f8fafc; border-radius: 8px; border: 1px solid #e2e8f0; font-family: Arial, sans-serif;">
<thead>
<tr style="background-color: #e2e8f0;">
<th style="padding: 12px; text-align: left; font-size: 14px; font-weight: bold; color: #374151;">Datum</th>
<th style="padding: 12px; text-align: right; font-size: 14px; font-weight: bold; color: #374151;">Dauer</th>
<th style="padding: 12px; text-align: right; font-size: 14px; font-weight: bold; color: #374151;">Upload</th>
<th style="padding: 12px; text-align: right; font-size: 14px; font-weight: bold; color: #374151;">Download</th>
<th style="padding: 12px; text-align: right; font-size: 14px; font-weight: bold; color: #374151;">Gesamt</th>
</tr>
</thead>
<tbody>
{$dailyDetailsTable}
</tbody>
</table>
<p style="font-family: Arial, sans-serif; font-size: 14px; color: #6b7280; margin: 24px 0 0; line-height: 1.5;">Bei Fragen zu Ihrer Statistik stehen wir Ihnen gerne zur Verfügung.</p>
</td>
</tr>
<tr>
<td style="padding: 25px 40px; text-align: center; font-family: Arial, sans-serif; font-size: 12px; color: #6b7280; border-top: 1px solid #eeeeee; background-color: #fafafa; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
<p style="margin: 0 0 10px 0;"><strong>XINON GmbH</strong></p>
<p style="margin: 0 0 5px 0;">Fladnitz im Raabtal 150 | A-8322 Studenzen</p>
<p style="margin: 0 0 15px 0;">
<a href="tel:+43311540800" style="color: {$xinonBlue}; text-decoration: none;">+43 3115 40800</a> |
<a href="mailto:office@xinon.at" style="color: {$xinonBlue}; text-decoration: none;">office@xinon.at</a>
</p>
<p style="margin: 0;">&copy; {$currentYear} XINON GmbH | <a href="https://xinon.at/impressum/" target="_blank" style="color: {$xinonBlue}; text-decoration: none;">Impressum</a></p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
HTML;
$altBody = "Ihre Transfer-Statistik für {$monthName} {$year}\n\n" .
"Sehr geehrter Kunde,\n\n" .
"anbei finden Sie eine Übersicht Ihrer Netzwerk-Nutzung für {$monthName} {$year}.\n\n" .
"Monatszusammenfassung:\n" .
"Gesamt: {$monthlyTotal}\n" .
"Download: {$monthlyDownload}\n" .
"Upload: {$monthlyUpload}\n" .
"Dauer: {$monthlyDuration}\n\n" .
"Eine detaillierte tägliche Aufstellung finden Sie in der HTML-Version dieser E-Mail.\n\n" .
"Bei Fragen zu Ihrer Statistik stehen wir Ihnen gerne zur Verfügung.\n\n" .
"© {$currentYear} XINON GmbH | Impressum: https://xinon.at/impressum/";
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = TT_PIPEWORK_SMTP_HOST;
$mail->SMTPAuth = true;
$mail->Username = TT_PIPEWORK_SMTP_USER;
$mail->Password = TT_PIPEWORK_SMTP_PASS;
$mail->CharSet = PHPMailer::CHARSET_UTF8;
$mail->Encoding = 'base64';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
if (file_exists($logoToolPath)) $mail->addEmbeddedImage($logoToolPath, 'logo_thetool');
if (file_exists($logoXinonPath)) $mail->addEmbeddedImage($logoXinonPath, 'logo_xinon');
if (!empty($chartImage) && ($imageData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $chartImage)))) {
$mail->addStringEmbeddedImage($imageData, 'chart_image', 'chart.png', 'base64', 'image/png');
}
$mail->setFrom('thetool@xinon.at', 'TheTOOL by XINON');
$mail->addReplyTo('office@xinon.at', 'XINON Office');
$mail->addAddress($recipient, $customerNumber);
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = $html;
$mail->AltBody = $altBody;
$mail->send();
self::returnJson(['success' => true, 'message' => 'Transfer-Statistik E-Mail wurde erfolgreich gesendet.']);
} catch (Exception $e) {
error_log("Mailer Error in sendCustomerEmailAction for username {$username}: " . $mail->ErrorInfo);
self::sendError("E-Mail konnte nicht gesendet werden. Bitte kontaktieren Sie den Support.");
}
}
/**
* Get GenieACS instance
* @return GenieACS
*/
private function getGenieACS() {
$host = defined('GENIEACS_HOST') ? GENIEACS_HOST : 'http://acs.xinon.at:3000';
$username = defined('GENIEACS_USERNAME') ? GENIEACS_USERNAME : 'admin';
$password = defined('GENIEACS_PASSWORD') ? GENIEACS_PASSWORD : 'savemanfb545aw';
return new GenieACS($host, $username, $password);
}
/**
* Check if GenieACS task response indicates success
* @param mixed $result
* @return bool
*/
private static function isGenieAcsTaskSuccess($result) {
// Null or empty array can indicate success (204 No Content)
if ($result === null || (is_array($result) && empty($result))) {
return true;
}
// Has explicit success flag
if (isset($result['success']) && $result['success']) {
return true;
}
// Array of tasks with _id (task created successfully)
if (is_array($result)) {
// Check if it's an array of task objects
foreach ($result as $item) {
if (is_array($item) && isset($item['_id'])) {
return true; // Task was created
}
}
}
return false;
}
/**
* Get devices from GenieACS and match by IP
*/
protected function genieacsGetDeviceByIpAction() {
try {
$ip = $_GET['ip'] ?? null;
if (!$ip) {
self::sendError("IP address is required");
}
$acs = $this->getGenieACS();
$devices = $acs->getDevices();
if (!$devices || !is_array($devices)) {
self::returnJson(['success' => false, 'message' => 'No devices found']);
return;
}
// Find device by matching external IP
$matchedDevice = null;
foreach ($devices as $device) {
$externalIp = GenieACS::getExternalIP($device);
if ($externalIp === $ip) {
$matchedDevice = $device;
break;
}
}
if (!$matchedDevice) {
self::returnJson(['success' => false, 'message' => 'No device found with this IP']);
return;
}
$deviceId = GenieACS::getDeviceId($matchedDevice);
$deviceInfo = GenieACS::getDeviceInfo($matchedDevice);
$managementIp = GenieACS::getManagementIP($matchedDevice);
self::returnJson([
'success' => true,
'deviceId' => $deviceId,
'deviceInfo' => $deviceInfo,
'ip' => $ip,
'managementIp' => $managementIp
]);
} catch (Exception $e) {
error_log("GenieACS getDeviceByIp error: " . $e->getMessage());
self::sendError("Error fetching device: " . $e->getMessage());
}
}
/**
* Reboot device via GenieACS
*/
protected function genieacsRebootDeviceAction() {
try {
$input = json_decode(file_get_contents('php://input'), true);
$deviceId = $input['deviceId'] ?? null;
if (!$deviceId) {
self::sendError("Device ID is required");
}
error_log("GenieACS reboot request for device: " . $deviceId);
$acs = $this->getGenieACS();
$result = $acs->rebootDevice($deviceId);
error_log("GenieACS reboot result: " . json_encode($result));
if (self::isGenieAcsTaskSuccess($result)) {
self::returnJson(['success' => true, 'message' => 'Reboot task created', 'result' => $result]);
} else {
self::returnJson(['success' => false, 'message' => 'Failed to create reboot task', 'result' => $result]);
}
} catch (Exception $e) {
error_log("GenieACS rebootDevice error: " . $e->getMessage());
self::sendError("Error rebooting device: " . $e->getMessage());
}
}
/**
* Refresh device information
*/
protected function genieacsRefreshDeviceAction() {
try {
$input = json_decode(file_get_contents('php://input'), true);
$deviceId = $input['deviceId'] ?? null;
if (!$deviceId) {
self::sendError("Device ID is required");
}
$acs = $this->getGenieACS();
$result = $acs->refreshDevice($deviceId);
if (self::isGenieAcsTaskSuccess($result)) {
self::returnJson(['success' => true, 'message' => 'Refresh task created', 'result' => $result]);
} else {
self::returnJson(['success' => false, 'message' => 'Failed to create refresh task', 'result' => $result]);
}
} catch (Exception $e) {
error_log("GenieACS refreshDevice error: " . $e->getMessage());
self::sendError("Error refreshing device: " . $e->getMessage());
}
}
/**
* Get device parameters
*/
protected function genieacsGetParametersAction() {
try {
$input = json_decode(file_get_contents('php://input'), true);
$deviceId = $input['deviceId'] ?? null;
$parameters = $input['parameters'] ?? [];
if (!$deviceId || empty($parameters)) {
self::sendError("Device ID and parameters are required");
}
$acs = $this->getGenieACS();
$result = $acs->getParameterValues($deviceId, $parameters);
// Check if result is a successful task response
if (self::isGenieAcsTaskSuccess($result)) {
self::returnJson(['success' => true, 'message' => 'Get parameters task created', 'result' => $result]);
} else {
self::returnJson(['success' => false, 'message' => 'Failed to create get parameters task', 'result' => $result]);
}
} catch (Exception $e) {
error_log("GenieACS getParameters error: " . $e->getMessage());
self::sendError("Error getting parameters: " . $e->getMessage());
}
}
/**
* Set device parameters
*/
protected function genieacsSetParametersAction() {
try {
$input = json_decode(file_get_contents('php://input'), true);
$deviceId = $input['deviceId'] ?? null;
$parameters = $input['parameters'] ?? [];
if (!$deviceId || empty($parameters)) {
self::sendError("Device ID and parameters are required");
}
$acs = $this->getGenieACS();
$result = $acs->setParameterValues($deviceId, $parameters);
if (self::isGenieAcsTaskSuccess($result)) {
self::returnJson(['success' => true, 'message' => 'Set parameters task created', 'result' => $result]);
} else {
self::returnJson(['success' => false, 'message' => 'Failed to create set parameters task', 'result' => $result]);
}
} catch (Exception $e) {
error_log("GenieACS setParameters error: " . $e->getMessage());
self::sendError("Error setting parameters: " . $e->getMessage());
}
}
/**
* Get full device information
*/
protected function genieacsGetDeviceInfoAction() {
try {
$deviceId = $_GET['deviceId'] ?? null;
if (!$deviceId) {
self::sendError("Device ID is required");
}
$acs = $this->getGenieACS();
$device = $acs->getDevice($deviceId);
if (!$device) {
self::sendError("Device not found");
}
$deviceInfo = GenieACS::getDeviceInfo($device);
$externalIp = GenieACS::getExternalIP($device);
$macAddress = GenieACS::getMacAddress($device);
self::returnJson([
'success' => true,
'deviceInfo' => $deviceInfo,
'externalIp' => $externalIp,
'macAddress' => $macAddress,
'fullData' => $device
]);
} catch (Exception $e) {
error_log("GenieACS getDeviceInfo error: " . $e->getMessage());
self::sendError("Error getting device info: " . $e->getMessage());
}
}
/**
* Ping an IP address via GenieACS
*/
protected function genieacsPingAction() {
try {
$ip = $_GET['ip'] ?? null;
if (!$ip) {
self::sendError("IP address is required");
}
$acs = $this->getGenieACS();
$result = $acs->ping($ip);
self::returnJson(['success' => true, 'result' => $result]);
} catch (Exception $e) {
error_log("GenieACS ping error: " . $e->getMessage());
self::sendError("Error pinging: " . $e->getMessage());
}
}
/**
* Factory reset device via GenieACS
*/
protected function genieacsFactoryResetAction() {
try {
$input = json_decode(file_get_contents('php://input'), true);
$deviceId = $input['deviceId'] ?? null;
if (!$deviceId) {
self::sendError("Device ID is required");
}
$acs = $this->getGenieACS();
$result = $acs->factoryResetDevice($deviceId);
if (self::isGenieAcsTaskSuccess($result)) {
self::returnJson(['success' => true, 'message' => 'Factory reset task created', 'result' => $result]);
} else {
self::returnJson(['success' => false, 'message' => 'Failed to create factory reset task', 'result' => $result]);
}
} catch (Exception $e) {
error_log("GenieACS factoryReset error: " . $e->getMessage());
self::sendError("Error factory resetting device: " . $e->getMessage());
}
}
}