api.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. <?php
  2. require_once ($_SERVER['DOCUMENT_ROOT']."/inc/auth.utils.php");
  3. login($db_link);
  4. //error_log("GET: " . json_encode($_GET));
  5. //error_log("POST: " . json_encode($_POST));
  6. // Получаем параметры через безопасные функции
  7. $action_get = getParam('get', null, null);
  8. $action_send = getParam('send', null, null);
  9. $ip = getParam('ip', null, null, FILTER_VALIDATE_IP, ['flags' => FILTER_FLAG_IPV4]);
  10. $mac_raw = getParam('mac', null, null);
  11. $rec_id = getParam('id', null, null, FILTER_VALIDATE_INT);
  12. $f_subnet = getParam('subnet', null, null);
  13. $description = getParam('description', null, null);
  14. // Преобразуем IP в BIGINT
  15. $ip_aton = null;
  16. if (!empty($ip)) {
  17. $ip_aton = sprintf('%u', ip2long($ip));
  18. }
  19. // Новые параметры для универсальных методов
  20. $table = getParam('table', null, null);
  21. $filter = getParam('filter', null, null); // JSON-строка для кастомного фильтра
  22. $update_data = null;
  23. $content_type = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '';
  24. if (stripos($content_type, 'application/json') !== false) {
  25. $raw_input = file_get_contents('php://input');
  26. if ($raw_input) {
  27. $json_data = json_decode($raw_input, true);
  28. if (json_last_error() === JSON_ERROR_NONE) {
  29. $update_data = $raw_input; // Передаём как строку для дальнейшей обработки
  30. }
  31. }
  32. }
  33. // Если не получили из тела запроса, пытаемся получить из параметров
  34. if ($update_data === null) {
  35. $update_data = getParam('data', null, null);
  36. }
  37. // Параметры пагинации
  38. $limit_param = getParam('limit', null, null, FILTER_VALIDATE_INT);
  39. $offset_param = getParam('offset', null, null, FILTER_VALIDATE_INT);
  40. $limit = ($limit_param !== null && $limit_param > 0) ? min((int)$limit_param, 1000) : 1000;
  41. $offset = ($offset_param !== null && $offset_param >= 0) ? (int)$offset_param : 0;
  42. if (needs_decoding($f_subnet)) { $f_subnet = rawurldecode($f_subnet); }
  43. // Обработка MAC-адреса
  44. $mac = '';
  45. if (!empty($mac_raw) && checkValidMac($mac_raw)) {
  46. $mac = mac_dotted(trim($mac_raw));
  47. }
  48. // Определяем действие
  49. $action = '';
  50. if (!empty($action_get)) { $action = 'get_' . $action_get; }
  51. if (!empty($action_send)) { $action = 'send_' . $action_send; }
  52. // Дополнительные параметры для send_dhcp
  53. $dhcp_hostname = getParam('hostname', null, '');
  54. $dhcp_action = getParam('action', null, 1, FILTER_VALIDATE_INT);
  55. // === Список разрешённых таблиц ===
  56. $allowed_tables = [
  57. 'building',
  58. 'devices',
  59. 'device_models',
  60. 'device_ports',
  61. 'connections',
  62. 'ou',
  63. 'queue_list',
  64. 'group_list',
  65. 'subnets',
  66. 'config',
  67. 'user_auth',
  68. 'user_list',
  69. 'vendors'
  70. ];
  71. function log_api_call($action, $params = []) {
  72. global $db_link;
  73. $log_data = [
  74. 'action' => $action,
  75. 'params' => $params,
  76. 'timestamp' => date('Y-m-d H:i:s'),
  77. 'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
  78. 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
  79. ];
  80. LOG_DEBUG($db_link, "API CALL: " . json_encode($log_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
  81. }
  82. function do_exit() {
  83. exit;
  84. }
  85. // === Валидация таблицы ===
  86. function validate_table($table_name, $allowed) {
  87. return in_array($table_name, $allowed) ? $table_name : null;
  88. }
  89. // === Безопасное получение данных из таблицы ===
  90. function safe_get_records($db, $table, $filter = null, $limit = 1000, $offset = 0) {
  91. global $allowed_tables;
  92. if (!validate_table($table, $allowed_tables)) {
  93. return ['error' => 'Invalid table name'];
  94. }
  95. $sql = "SELECT * FROM " . $table;
  96. $params = [];
  97. if ($filter) {
  98. // Фильтр в формате: {"field":"value","field2":"value2"}
  99. $filter_arr = json_decode($filter, true);
  100. if (is_array($filter_arr) && !empty($filter_arr)) {
  101. $conditions = [];
  102. foreach ($filter_arr as $field => $value) {
  103. // Защита от SQL-инъекции: проверяем имя поля
  104. if (!preg_match('/^[a-z_][a-z0-9_]*$/i', $field)) {
  105. continue;
  106. }
  107. $conditions[] = "$field = ?";
  108. $params[] = $value;
  109. }
  110. if (!empty($conditions)) {
  111. $sql .= " WHERE " . implode(" AND ", $conditions);
  112. }
  113. }
  114. }
  115. $sql .= " LIMIT " . (int)$limit;
  116. if ($offset > 0) {
  117. $sql .= " OFFSET " . (int)$offset;
  118. }
  119. $result = get_records_sql($db, $sql, $params);
  120. return $result;
  121. }
  122. // === Безопасное получение одной записи ===
  123. function safe_get_record($db, $table, $id) {
  124. global $allowed_tables;
  125. if (!validate_table($table, $allowed_tables)) {
  126. return ['error' => 'Invalid table name'];
  127. }
  128. if (!is_numeric($id) || $id <= 0) {
  129. return ['error' => 'Invalid ID'];
  130. }
  131. $pk_field = 'id'; // Все таблицы используют 'id' как первичный ключ
  132. $result = get_record_sql($db, "SELECT * FROM $table WHERE $pk_field = ?", [(int)$id]);
  133. # error_log("SELECT * FROM $table WHERE $pk_field = $id ::". $result);
  134. return $result;
  135. }
  136. if (!empty($action)) {
  137. // === УНИВЕРСАЛЬНЫЙ МЕТОД: get_table_record ===
  138. if ($action === 'get_table_record' && !empty($table)) {
  139. log_api_call($action, [
  140. 'table' => $table,
  141. 'filter' => $filter,
  142. 'limit' => $limit,
  143. 'offset' => $offset,
  144. 'id' => $rec_id
  145. ]);
  146. if ($rec_id>0) {
  147. $result = safe_get_record($db_link, $table, $rec_id);
  148. } elseif (!empty($filter)) {
  149. $result_arr = safe_get_records($db_link, $table, $filter, 1);
  150. if (!empty($result_arr)) { $result = $result_arr[0]; }
  151. } else {
  152. do_exit();
  153. }
  154. if (isset($result['error'])) {
  155. http_response_code(400);
  156. echo json_encode($result);
  157. do_exit();
  158. }
  159. if ($result) {
  160. header('Content-Type: application/json; charset=utf-8');
  161. echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  162. } else {
  163. http_response_code(404);
  164. echo json_encode(['error' => 'Record not found']);
  165. }
  166. do_exit();
  167. }
  168. // === УНИВЕРСАЛЬНЫЙ МЕТОД: get_table_list ===
  169. if ($action === 'get_table_list' && !empty($table)) {
  170. log_api_call($action, [
  171. 'table' => $table,
  172. 'filter' => $filter,
  173. 'limit' => $limit,
  174. 'offset' => $offset,
  175. ]);
  176. $result = safe_get_records($db_link, $table, $filter, $limit, $offset);
  177. if (isset($result['error'])) {
  178. http_response_code(400);
  179. echo json_encode($result);
  180. do_exit();
  181. }
  182. header('Content-Type: application/json; charset=utf-8');
  183. echo json_encode($result ?: [], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  184. do_exit();
  185. }
  186. // === ОБНОВЛЕНИЕ USER_LIST ===
  187. if ($action === 'send_update_user' && $rec_id > 0 && !empty($update_data)) {
  188. log_api_call($action, [
  189. 'rec_id' => $rec_id,
  190. 'update_data' => $update_data,
  191. ]);
  192. $data = json_decode($update_data, true);
  193. if (!is_array($data)) {
  194. http_response_code(400);
  195. echo json_encode(['error' => 'Invalid data format']);
  196. do_exit();
  197. }
  198. // Разрешённые поля для обновления
  199. $allowed_fields = [
  200. 'login', 'description', 'enabled', 'blocked', 'ou_id',
  201. 'filter_group_id', 'queue_id', 'day_quota', 'month_quota', 'permanent'
  202. ];
  203. $update_fields = [];
  204. foreach ($data as $key => $value) {
  205. if (in_array($key, $allowed_fields)) {
  206. $update_fields[$key] = $value;
  207. }
  208. }
  209. if (empty($update_fields)) {
  210. http_response_code(400);
  211. echo json_encode(['error' => 'No valid fields to update']);
  212. do_exit();
  213. }
  214. // Проверяем существование пользователя
  215. $existing = get_record_sql($db_link, "SELECT id FROM user_list WHERE id = ?", [$rec_id]);
  216. if (!$existing) {
  217. http_response_code(404);
  218. echo json_encode(['error' => 'User not found']);
  219. do_exit();
  220. }
  221. // Выполняем обновление
  222. if (update_record($db_link, 'user_list', 'id = ?', $update_fields, [$rec_id])) {
  223. LOG_VERBOSE($db_link, "API: User $rec_id updated successfully");
  224. http_response_code(200);
  225. echo json_encode(['status' => 'updated', 'id' => $rec_id]);
  226. } else {
  227. LOG_ERROR($db_link, "API: Failed to update user $rec_id");
  228. http_response_code(500);
  229. echo json_encode(['error' => 'Update failed']);
  230. }
  231. do_exit();
  232. }
  233. // === ОБНОВЛЕНИЕ USER_AUTH ===
  234. if ($action === 'send_update_user_auth' && $rec_id > 0 && !empty($update_data)) {
  235. log_api_call($action, [
  236. 'rec_id' => $rec_id,
  237. 'update_data' => $update_data,
  238. ]);
  239. $data = json_decode($update_data, true);
  240. if (!is_array($data)) {
  241. http_response_code(400);
  242. echo json_encode(['error' => 'Invalid data format']);
  243. do_exit();
  244. }
  245. // Разрешённые поля для обновления
  246. $allowed_fields = ['mac', 'ip', 'ip_int', 'wikiname', 'description', 'dns_name'];
  247. $update_fields = [];
  248. foreach ($data as $key => $value) {
  249. $db_key = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $key)); // WikiName -> wiki_name
  250. if (in_array($db_key, $allowed_fields)) {
  251. $update_fields[$db_key] = $value;
  252. }
  253. }
  254. if (empty($update_fields)) {
  255. http_response_code(400);
  256. echo json_encode(['error' => 'No valid fields to update']);
  257. do_exit();
  258. }
  259. if (update_record($db_link, 'user_auth', 'id = ?', $update_fields, [$rec_id])) {
  260. LOG_VERBOSE($db_link, "API: User_auth $rec_id updated via API", $rec_id);
  261. http_response_code(200);
  262. echo json_encode(['status' => 'updated', 'id' => $rec_id]);
  263. } else {
  264. LOG_ERROR($db_link, "API: Failed to update user_auth $rec_id", $rec_id);
  265. http_response_code(500);
  266. echo json_encode(['error' => 'Update failed']);
  267. }
  268. do_exit();
  269. }
  270. // === get_user_auth ===
  271. if ($action === 'get_user_auth') {
  272. LOG_VERBOSE($db_link, "API: Get User Auth record with ip: $ip mac: $mac id: $rec_id");
  273. log_api_call($action, [
  274. 'ip' => $ip,
  275. 'ip_aton' => $ip_aton,
  276. 'mac' => $mac,
  277. 'rec_id' => $rec_id,
  278. ]);
  279. $result = null;
  280. $sql = "";
  281. $params = [];
  282. if ($rec_id > 0) {
  283. $sql = "SELECT * FROM user_auth WHERE id = ?";
  284. $params = [$rec_id];
  285. } elseif ($ip_aton !== null && !empty($mac)) {
  286. $sql = "SELECT * FROM user_auth WHERE ip_int = ? AND mac = ? AND deleted = 0";
  287. $params = [$ip_aton, $mac];
  288. } elseif ($ip_aton !== null) {
  289. $sql = "SELECT * FROM user_auth WHERE ip_int = ? AND deleted = 0";
  290. $params = [$ip_aton];
  291. } elseif (!empty($mac)) {
  292. $sql = "SELECT * FROM user_auth WHERE mac = ? AND deleted = 0";
  293. $params = [$mac];
  294. }
  295. if ($sql) {
  296. $result = get_record_sql($db_link, $sql, $params);
  297. if ($result) {
  298. LOG_VERBOSE($db_link, "API: Record found.");
  299. header('Content-Type: application/json; charset=utf-8');
  300. echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  301. } else {
  302. LOG_VERBOSE($db_link, "API: Record not found.");
  303. http_response_code(404);
  304. echo json_encode(['error' => 'Not found']);
  305. }
  306. } else {
  307. LOG_DEBUG($db_link, "API: not enough parameters | GET: " . json_encode($_GET, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . " | POST: " . json_encode($_POST, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
  308. http_response_code(400);
  309. echo json_encode(['error' => 'Missing parameters']);
  310. }
  311. do_exit();
  312. }
  313. // === get_user ===
  314. if ($action === 'get_user') {
  315. LOG_VERBOSE($db_link, "API: Get User record with id: $rec_id");
  316. log_api_call($action, [
  317. 'rec_id' => $rec_id,
  318. ]);
  319. if ($rec_id > 0) {
  320. $user = get_record_sql($db_link, "SELECT * FROM user_list WHERE deleted = 0 AND id = ?", [$rec_id]);
  321. if ($user) {
  322. $auth_records = get_records_sql($db_link,
  323. "SELECT * FROM user_auth WHERE deleted = 0 AND user_id = ? ORDER BY id LIMIT 100",
  324. [$rec_id]
  325. );
  326. $user['auth'] = $auth_records ?: [];
  327. LOG_VERBOSE($db_link, "API: User record found.");
  328. header('Content-Type: application/json; charset=utf-8');
  329. echo json_encode($user, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  330. } else {
  331. LOG_VERBOSE($db_link, "API: User not found.");
  332. http_response_code(404);
  333. echo json_encode(['error' => 'User not found']);
  334. }
  335. } else {
  336. LOG_VERBOSE($db_link, "API: not enough parameters");
  337. http_response_code(400);
  338. echo json_encode(['error' => 'Missing user ID']);
  339. }
  340. do_exit();
  341. }
  342. // === get_dhcp_all ===
  343. if ($action === 'get_dhcp_all') {
  344. LOG_VERBOSE($db_link, "API: Get all dhcp records");
  345. $result = get_records_sql($db_link, "
  346. SELECT
  347. ua.id, ua.ip, ua.ip_int, ua.mac, ua.description,
  348. ua.dns_name, ua.dhcp_option_set, ua.dhcp_acl, ua.ou_id,
  349. SUBSTRING_INDEX(s.subnet, '/', 1) AS subnet_base
  350. FROM user_auth ua
  351. JOIN subnets s ON ua.ip_int BETWEEN s.ip_int_start AND s.ip_int_stop
  352. WHERE ua.dhcp = 1 AND ua.deleted = 0 AND s.dhcp = 1
  353. ORDER BY ua.ip_int
  354. LIMIT ? OFFSET ?
  355. ", [$limit, $offset]);
  356. LOG_VERBOSE($db_link, "API: " . count($result) . " records found.");
  357. header('Content-Type: application/json; charset=utf-8');
  358. echo json_encode($result ?: [], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  359. do_exit();
  360. }
  361. // === get_dhcp_subnet ===
  362. if ($action === 'get_dhcp_subnet' && !empty($f_subnet)) {
  363. log_api_call($action, [
  364. 'subnet' => $f_subnet,
  365. ]);
  366. $subnet = cidrToRange($f_subnet);
  367. if (empty($subnet)) {
  368. http_response_code(400);
  369. echo json_encode(['error' => 'Invalid subnet format']);
  370. do_exit();
  371. }
  372. LOG_VERBOSE($db_link, "API: Get dhcp records for subnet " . $f_subnet);
  373. $result = get_records_sql($db_link, "
  374. SELECT
  375. ua.id, ua.ip, ua.ip_int, ua.mac, ua.description,
  376. ua.dns_name, ua.dhcp_option_set, ua.dhcp_acl, ua.ou_id,
  377. SUBSTRING_INDEX(s.subnet, '/', 1) AS subnet_base
  378. FROM user_auth ua
  379. JOIN subnets s ON ua.ip_int BETWEEN s.ip_int_start AND s.ip_int_stop
  380. WHERE ua.dhcp = 1 AND ua.deleted = 0 AND s.dhcp = 1
  381. AND SUBSTRING_INDEX(s.subnet, '/', 1) = ?
  382. ORDER BY ua.ip_int
  383. LIMIT ? OFFSET ?
  384. ", [$f_subnet, $limit, $offset]);
  385. LOG_VERBOSE($db_link, "API: " . count($result) . " records found.");
  386. header('Content-Type: application/json; charset=utf-8');
  387. echo json_encode($result ?: [], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  388. do_exit();
  389. }
  390. // === get_user_subnet ===
  391. if ($action === 'get_user_subnet' && !empty($f_subnet)) {
  392. LOG_VERBOSE($db_link, "API: Get ip records for subnet " . $f_subnet);
  393. log_api_call($action, [
  394. 'subnet' => $f_subnet,
  395. ]);
  396. $subnet = cidrToRange($f_subnet);
  397. if (empty($subnet)) {
  398. http_response_code(400);
  399. echo json_encode(['error' => 'Invalid subnet format']);
  400. do_exit();
  401. }
  402. $first_ip = ip2long($subnet[0]);
  403. $last_ip = ip2long($subnet[1]);
  404. $result = get_records_sql($db_link, "
  405. SELECT id, ip, ip_int, mac, description, dns_name, dhcp_hostname, ou_id
  406. FROM user_auth
  407. WHERE deleted=0 AND ip_int BETWEEN ? AND ? ORDER BY ip_int
  408. LIMIT ? OFFSET ?
  409. ", [ $first_ip, $last_ip, $limit, $offset]);
  410. LOG_VERBOSE($db_link, "API: " . count($result) . " records found.");
  411. header('Content-Type: application/json; charset=utf-8');
  412. echo json_encode($result ?: [], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  413. do_exit();
  414. }
  415. // === send_dhcp ===
  416. if ($action === 'send_dhcp') {
  417. log_api_call($action, [
  418. 'ip' => $ip,
  419. 'mac' => $mac,
  420. 'action' => $dhcp_action,
  421. ]);
  422. if ($ip && $mac) {
  423. $faction = $dhcp_action !== null ? (int)$dhcp_action : 1;
  424. $action_str = ($faction === 0) ? 'del' : 'add';
  425. LOG_VERBOSE($db_link, "API: external dhcp request for $ip [$mac] $action_str");
  426. if (is_our_network($db_link, $ip)) {
  427. $dhcp_record = [
  428. 'action' => $action_str,
  429. 'mac' => $mac,
  430. 'ip' => $ip,
  431. 'dhcp_hostname' => $dhcp_hostname
  432. ];
  433. $insert_id = insert_record($db_link, "dhcp_queue", $dhcp_record);
  434. if ($insert_id !== false && $insert_id > 0) {
  435. LOG_VERBOSE($db_link, "API: DHCP record queued successfully. ID: $insert_id, IP: $ip, MAC: $mac, Action: $action_str");
  436. http_response_code(201);
  437. echo json_encode([
  438. 'status' => 'queued',
  439. 'id' => (int)$insert_id,
  440. 'ip' => $ip,
  441. 'mac' => $mac,
  442. 'action' => $action_str
  443. ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  444. } else {
  445. $error_msg = "Failed to insert DHCP record into queue";
  446. LOG_ERROR($db_link, "API: $error_msg. IP: $ip, MAC: $mac, Action: $action_str");
  447. http_response_code(500);
  448. echo json_encode([
  449. 'error' => $error_msg,
  450. 'ip' => $ip,
  451. 'mac' => $mac
  452. ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  453. }
  454. } else {
  455. $error_msg = "IP not in allowed network";
  456. LOG_ERROR($db_link, "API: $error_msg - $ip [$mac]");
  457. http_response_code(400);
  458. echo json_encode([
  459. 'error' => $error_msg,
  460. 'ip' => $ip
  461. ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  462. }
  463. } else {
  464. $missing_params = [];
  465. if (!$ip) $missing_params[] = 'ip';
  466. if (!$mac) $missing_params[] = 'mac';
  467. $error_msg = 'Missing required parameters: ' . implode(', ', $missing_params);
  468. LOG_WARNING($db_link, "API: send_dhcp called with missing parameters. Missing: " . implode(', ', $missing_params));
  469. http_response_code(400);
  470. echo json_encode([
  471. 'error' => $error_msg
  472. ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  473. }
  474. do_exit();
  475. }
  476. // === send_user_ip ===
  477. if ($action === 'send_user_ip') {
  478. log_api_call($action, [
  479. 'ip' => $ip,
  480. 'mac' => $mac,
  481. 'action' => $dhcp_action,
  482. 'description' => $description
  483. ]);
  484. if ($ip) {
  485. $faction = $dhcp_action !== null ? (int)$dhcp_action : 1;
  486. $action_str = ($faction === 0) ? 'del' : 'add';
  487. LOG_VERBOSE($db_link, "API: external add auth request for $ip [$mac] $action_str");
  488. if (is_our_network($db_link, $ip)) {
  489. $ip_record = [
  490. 'action' => $action_str,
  491. 'mac' => $mac,
  492. 'ip' => $ip,
  493. 'description' => $description,
  494. 'dhcp_hostname' => $dhcp_hostname
  495. ];
  496. $auth_id = resurrection_auth($db_link, $ip_record);
  497. if ($auth_id !== false && $auth_id > 0) {
  498. http_response_code(201);
  499. echo json_encode([
  500. 'status' => 'queued',
  501. 'id' => (int)$auth_id,
  502. 'ip' => $ip,
  503. 'action' => $action_str
  504. ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  505. } else {
  506. $error_msg = "Failed to add ip record";
  507. LOG_ERROR($db_link, "API: $error_msg. IP: $ip, MAC: $mac, Action: $action_str");
  508. http_response_code(500);
  509. echo json_encode([
  510. 'error' => $error_msg,
  511. 'ip' => $ip,
  512. 'mac' => $mac
  513. ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  514. }
  515. } else {
  516. $error_msg = "IP not in allowed network";
  517. LOG_ERROR($db_link, "API: $error_msg - $ip [$mac]");
  518. http_response_code(400);
  519. echo json_encode([
  520. 'error' => $error_msg,
  521. 'ip' => $ip
  522. ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  523. }
  524. } else {
  525. $missing_params = [];
  526. if (!$ip) $missing_params[] = 'ip';
  527. $error_msg = 'Missing required parameters: ' . implode(', ', $missing_params);
  528. LOG_WARNING($db_link, "API: send_auth called with missing parameters. Missing: " . implode(', ', $missing_params));
  529. http_response_code(400);
  530. echo json_encode([
  531. 'error' => $error_msg
  532. ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
  533. }
  534. do_exit();
  535. }
  536. } else {
  537. LOG_WARNING($db_link, "API: Unknown request");
  538. http_response_code(400);
  539. echo json_encode(['error' => 'Unknown action']);
  540. }
  541. do_exit();
  542. ?>