initial commit api Investigator

This commit is contained in:
Luca Haid
2026-01-28 22:50:16 +01:00
parent 5cce3cf8ae
commit a5c82bbf24
2 changed files with 265 additions and 18 deletions

View File

@@ -0,0 +1,176 @@
<?php
class InvestigatorApicontroller extends mfBaseApicontroller
{
protected function init() {}
protected function registerRoutes()
{
$this->addRoute("/investigator/customer/:id", "getCustomer", "GET");
$this->addRoute("/investigator/customer/:id/contract", "getCustomerContract", "GET");
$this->addRoute("/investigator/customer/:id/cpe", "getCustomerCpe", "GET");
$this->addRoute("/investigator/customer/:id/address", "getCustomerAddress", "GET");
$this->addRoute("/investigator/search/customer", "searchCustomer", "GET");
}
protected function authenticated()
{
$this->registerRoutes();
}
public function getCustomer($customerId)
{
if (!$customerId) return mfResponse::BadRequest(['message' => 'Customer ID is required']);
$addresses = AddressModel::search(['customer_number' => $customerId]);
if (empty($addresses)) return mfResponse::NotFound(['message' => 'Customer not found']);
$address = $addresses[0];
return mfResponse::Ok([
'customer' => [
'id' => $address->id,
'customerNumber' => $address->customer_number,
'company' => $address->company,
'firstName' => $address->firstname,
'lastName' => $address->lastname,
'email' => $address->email,
'phone' => $address->phone,
'street' => $address->street,
'zip' => $address->zip,
'city' => $address->city,
'country' => $address->country,
]
]);
}
public function getCustomerContract($customerId)
{
if (!$customerId) return mfResponse::BadRequest(['message' => 'Customer ID is required']);
$addresses = AddressModel::search(['customer_number' => $customerId]);
if (empty($addresses)) return mfResponse::NotFound(['message' => 'Customer not found']);
$contracts = ContractModel::search(['owner_id' => $addresses[0]->id]);
$contractData = [];
foreach ($contracts as $contract) {
$contractData[] = [
'contractId' => $contract->contract_id,
'productName' => $contract->product_name,
'productExternal' => $contract->product_external,
'price' => $contract->price,
'billingPeriod' => $contract->billing_period,
'orderDate' => $contract->order_date,
'finishDate' => $contract->finish_date,
'cancelDate' => $contract->cancel_date,
'slaId' => $contract->sla_id,
'status' => $contract->cancel_date ? 'Cancelled' : ($contract->finish_date ? 'Active' : 'Pending'),
];
}
return mfResponse::Ok([
'customerId' => $customerId,
'contractCount' => count($contractData),
'contracts' => $contractData,
]);
}
public function getCustomerCpe($customerId)
{
if (!$customerId) return mfResponse::BadRequest(['message' => 'Customer ID is required']);
$addresses = AddressModel::search(['customer_number' => $customerId]);
if (empty($addresses)) return mfResponse::NotFound(['message' => 'Customer not found']);
$db = $this->db();
$sql = "SELECT cp.* FROM Cpeprovisioning cp
INNER JOIN `Order` o ON cp.order_id = o.id
WHERE o.owner_id = " . intval($addresses[0]->id) . "
ORDER BY cp.id DESC LIMIT 10";
$res = $db->query($sql);
$cpeData = [];
while ($row = $db->fetch_object($res)) {
$cpeData[] = [
'orderId' => $row->order_id ?? null,
'routerType' => $row->routertype ?? null,
'routerConfigFinished' => (bool)($row->routerconfig_finished ?? false),
'mac' => $row->mac ?? null,
'ontSerial' => $row->ont_sn ?? null,
'wifiSsid' => $row->wifi_ssid ?? null,
'wifiPasswordSet' => !empty($row->wifi_pass),
'vlanPublic' => $row->vlan_public ?? null,
'vlanNat' => $row->vlan_nat ?? null,
'vlanIpv6' => $row->vlan_ipv6 ?? null,
'shipping' => (bool)($row->shipping ?? false),
];
}
return mfResponse::Ok([
'customerId' => $customerId,
'cpeCount' => count($cpeData),
'cpeProvisioning' => $cpeData,
]);
}
public function getCustomerAddress($customerId)
{
if (!$customerId) return mfResponse::BadRequest(['message' => 'Customer ID is required']);
$addresses = AddressModel::search(['customer_number' => $customerId]);
if (empty($addresses)) return mfResponse::NotFound(['message' => 'Customer not found']);
$address = $addresses[0];
return mfResponse::Ok([
'customerId' => $customerId,
'address' => [
'company' => $address->company,
'name' => trim(($address->firstname ?? '') . ' ' . ($address->lastname ?? '')),
'street' => $address->street,
'zip' => $address->zip,
'city' => $address->city,
'country' => $address->country,
'email' => $address->email,
'phone' => $address->phone,
]
]);
}
public function searchCustomer()
{
$searchTerm = $this->get['q'] ?? '';
$limit = intval($this->get['limit'] ?? 10);
if (empty($searchTerm)) return mfResponse::BadRequest(['message' => 'Search term required']);
$db = $this->db();
$searchTerm = $db->escape($searchTerm);
$sql = "SELECT * FROM Address
WHERE customer_number LIKE '%$searchTerm%'
OR CONCAT(firstname, ' ', lastname) LIKE '%$searchTerm%'
OR company LIKE '%$searchTerm%'
OR email LIKE '%$searchTerm%'
OR street LIKE '%$searchTerm%'
LIMIT $limit";
$results = $db->queryRows($sql);
$customers = [];
foreach ($results as $row) {
$customers[] = [
'customerId' => $row['customer_number'],
'name' => trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? '')) ?: $row['company'],
'email' => $row['email'],
'city' => $row['city'],
];
}
return mfResponse::Ok([
'searchTerm' => $searchTerm,
'count' => count($customers),
'customers' => $customers,
]);
}
}

View File

@@ -4,8 +4,44 @@ use PHPMailer\PHPMailer\Exception;
class RadiusController extends mfBaseController {
private User $me;
private bool $isApiCall = false;
private array $apiAllowedActions = [
'ProxyUnsecureHTTPRequestToRadius',
'GenieacsRunSpeedtest',
'GenieacsGetSpeedtestResult',
'GenieacsGetDeviceByIp',
'GenieacsGetDeviceByMac',
'GenieacsRefreshDevice',
'GenieacsRebootDevice',
'GenieacsGetDeviceInfo',
'GenieacsPing',
'GenieacsRemoteAccess',
'GenieacsEventLog',
'GenieacsNetworkStructure',
];
protected function init(): void {
$apiKey = $_SERVER['HTTP_X_API_KEY'] ?? null;
if ($apiKey && in_array($this->action, $this->apiAllowedActions)) {
$me = new User();
$me->loadByApikey($apiKey);
if ($me->id) {
$this->me = $me;
$this->isApiCall = true;
$this->needlogin = false;
if (!defined('INTERNAL_USER_ID')) {
define('INTERNAL_USER_ID', $me->id);
}
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, X-API-Key");
return;
}
}
$this->needlogin = true;
$me = new User();
$me->loadMe();
@@ -51,20 +87,32 @@ class RadiusController extends mfBaseController {
$acs = $this->getGenieACS();
// Set speedtest parameters on the device
$acs->setParameterValues($deviceId, [
$resolvedId = $this->resolveDeviceId($deviceId, $acs);
if (!$resolvedId) self::sendError("Device not found in GenieACS");
$acs->getParameterValues($resolvedId, [
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.Start',
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.StartBidirect',
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.WANAccess',
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.Result'
]);
sleep(2);
$acs->setParameterValues($resolvedId, [
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.Start' => 1,
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.StartBidirect' => 1,
'InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.WANAccess' => true
]);
// Get device and extract IP
$device = $acs->getDevice($deviceId);
$ip = GenieACS::getExternalIP($device);
sleep(3);
$device = $acs->getDevice($resolvedId);
$managementIp = GenieACS::getManagementIP($device);
$externalIp = GenieACS::getExternalIP($device);
$ip = $externalIp ?: $managementIp;
if (!$ip) self::sendError("Could not determine device IP");
// Trigger speedtest via external API
$url = "http://acs.xinon.at:5000/run-speedtest";
$apiKey = "2H9zWrgxPEJL9MZ1yTGtWh16cPCu0AsQ";
$data = json_encode(['ip' => $ip]);
@@ -84,9 +132,8 @@ class RadiusController extends mfBaseController {
if ($response === false) self::sendError("Failed to connect to speedtest server");
self::returnJson(['success' => true, 'message' => 'Speedtest started']);
self::returnJson(['success' => true, 'message' => 'Speedtest started', 'ip' => $ip, 'serverResponse' => json_decode($response, true)]);
} catch (Exception $e) {
$this->log->debug("Speedtest Error", ['error' => $e->getMessage()]);
self::sendError("Error running speedtest: " . $e->getMessage());
}
}
@@ -101,11 +148,12 @@ class RadiusController extends mfBaseController {
$acs = $this->getGenieACS();
// Request parameter refresh
$acs->getParameterValues($deviceId, ['InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.Result']);
$resolvedId = $this->resolveDeviceId($deviceId, $acs);
if (!$resolvedId) self::sendError("Device not found in GenieACS");
// Get device info with full data
$device = $acs->getDevice($deviceId);
$acs->getParameterValues($resolvedId, ['InternetGatewayDevice.X_AVM-DE_SpeedtestServer.UDP.Result']);
$device = $acs->getDevice($resolvedId);
if (!$device) self::sendError("Device not found");
@@ -178,6 +226,19 @@ class RadiusController extends mfBaseController {
return new GenieACS($host, $username, $password);
}
private function resolveDeviceId(string $deviceId, GenieACS $acs): ?string {
if (strpos($deviceId, ':') !== false) {
$device = $acs->getDeviceByMac($deviceId);
if ($device) {
$resolvedId = GenieACS::getDeviceId($device);
if ($resolvedId) return $resolvedId;
if (isset($device['_id'])) return $device['_id'];
}
return null;
}
return $deviceId;
}
protected function genieacsGetDeviceByIpAction() {
try {
$ip = $_GET['ip'] ?? null;
@@ -255,7 +316,11 @@ class RadiusController extends mfBaseController {
if (!$deviceId) self::sendError("Device ID is required");
$acs = $this->getGenieACS();
$acs->getParameterValues($deviceId, [
$resolvedId = $this->resolveDeviceId($deviceId, $acs);
if (!$resolvedId) self::sendError("Device not found in GenieACS");
$acs->getParameterValues($resolvedId, [
'InternetGatewayDevice.DeviceInfo.HardwareVersion',
'InternetGatewayDevice.DeviceInfo.SoftwareVersion',
'InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.MACAddress',
@@ -267,7 +332,7 @@ class RadiusController extends mfBaseController {
'InternetGatewayDevice.LANDevice.*.Hosts.Host.*.MACAddress'
]);
$device = $acs->getDevice($deviceId);
$device = $acs->getDevice($resolvedId);
self::returnJson([
'success' => true,
@@ -373,8 +438,11 @@ class RadiusController extends mfBaseController {
if (!$deviceId) self::sendError("Device ID is required");
$acs = $this->getGenieACS();
$creds = $acs->createRemoteUser($deviceId);
$resolvedId = $this->resolveDeviceId($deviceId, $acs);
if (!$resolvedId) self::sendError("Device not found in GenieACS");
$creds = $acs->createRemoteUser($resolvedId);
if (!$creds) self::sendError("Could not obtain credentials for FritzBox");
$url = "http://acs.xinon.at:5000/read-fritz-eventlog";
@@ -425,8 +493,11 @@ class RadiusController extends mfBaseController {
if (!$deviceId) self::sendError("Device ID is required");
$acs = $this->getGenieACS();
$creds = $acs->createRemoteUser($deviceId);
$resolvedId = $this->resolveDeviceId($deviceId, $acs);
if (!$resolvedId) self::sendError("Device not found in GenieACS");
$creds = $acs->createRemoteUser($resolvedId);
if (!$creds) self::sendError("Could not obtain credentials for FritzBox");
$url = "http://acs.xinon.at:5000/read-fritz";