瀏覽代碼

extend API for wiki support

Roman Dmitriev 2 月之前
父節點
當前提交
40f972f579

+ 41 - 0
html/api.php

@@ -43,6 +43,8 @@ $allowed_tables = [
     'building',
     'devices',
     'device_models',
+    'device_ports',
+    'connections',
     'ou',
     'queue_list',
     'group_list',
@@ -209,6 +211,45 @@ if (!empty($action)) {
         do_exit();
     }
 
+    // === ОБНОВЛЕНИЕ USER_AUTH ===
+    if ($action === 'send_update_user_auth' && $rec_id > 0 && !empty($update_data)) {
+        $data = json_decode($update_data, true);
+        
+        if (!is_array($data)) {
+            http_response_code(400);
+            echo json_encode(['error' => 'Invalid data format']);
+            do_exit();
+        }
+        
+        // Разрешённые поля для обновления
+        $allowed_fields = ['mac', 'ip', 'ip_int', 'wiki_name', 'description', 'dns_name'];
+        
+        $update_fields = [];
+        foreach ($data as $key => $value) {
+            $db_key = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $key)); // WikiName -> wiki_name
+            if (in_array($db_key, $allowed_fields)) {
+                $update_fields[$db_key] = $value;
+            }
+        }
+        
+        if (empty($update_fields)) {
+            http_response_code(400);
+            echo json_encode(['error' => 'No valid fields to update']);
+            do_exit();
+        }
+        
+        if (update_record($db_link, 'user_auth', 'id = ?', $update_fields, [$rec_id])) {
+            LOG_VERBOSE($db_link, "API: User_auth $rec_id updated via API");
+            http_response_code(200);
+            echo json_encode(['status' => 'updated', 'id' => $rec_id]);
+        } else {
+            LOG_ERROR($db_link, "API: Failed to update user_auth $rec_id");
+            http_response_code(500);
+            echo json_encode(['error' => 'Update failed']);
+        }
+        do_exit();
+    }
+
     // === get_user_auth ===
     if ($action === 'get_user_auth') {
         LOG_VERBOSE($db_link, "API: Get User Auth record with ip: $ip mac: $mac id: $rec_id");

+ 4 - 0
scripts/cfg/config.sample

@@ -41,3 +41,7 @@ nagios_event_socket=/var/spool/nagios4/hoststate.socket
 #crypt config - 8/16/24 char and equal length
 encryption_key=!!!CHANGE_ME!!!!
 encryption_iv=0123456789012345
+
+#api_base=http://127.0.0.1/api.php
+#api_key=<USER_KEY>
+#api_loign=<USER_LOGIN>

+ 4 - 0
scripts/eyelib/config.pm

@@ -170,6 +170,10 @@ $config_ref{nagios_event_socket}=$Config->{_}->{nagios_event_socket} || '/var/sp
 $config_ref{encryption_key}=$Config->{_}->{encryption_key} || '!!!CHANGE_ME!!!';
 $config_ref{encryption_iv}=$Config->{_}->{encryption_iv} || '123456782345';
 
+$config_ref{api_base}=$Config->{_}->{api_base} || 'http://127.0.0.1/api.php';
+$config_ref{api_login}=$Config->{_}->{api_login} || 'admin';
+$config_ref{api_key}=$Config->{_}->{api_key} || 'c3284d0f94606de1fd2af172aba15bf31';
+
 our $MY_NAME=$FN[-1];
 our $SPID=$config_ref{pid_file};
 

+ 0 - 1
scripts/eyelib/database.pm

@@ -80,7 +80,6 @@ init_db
 do_sql
 _execute_param
 do_sql_param
-get_option_safe
 get_count_records
 get_id_record
 get_records_sql

+ 134 - 0
scripts/utils/wiki/update_eye_wiki.pl

@@ -0,0 +1,134 @@
+#!/usr/bin/perl
+#
+# Синхронизация записей пользователей с файлами документации Wiki
+# через вызовы к PHP API (без прямого доступа к БД)
+#
+# Запуск: perl /path/to/update_eye_wiki.pl
+#
+
+use utf8;
+use warnings;
+use Encode;
+use open qw(:std :encoding(UTF-8));
+no warnings 'utf8';
+
+use strict;
+use English;
+use FindBin '$Bin';
+use lib "$Bin/";
+use Data::Dumper;
+use eyelib::config;
+use File::Find;
+use File::Basename;
+use Fcntl qw(:flock);
+use LWP::UserAgent;
+use HTTP::Request;
+use JSON::XS;
+use URI::Escape;
+use Encode qw(decode_utf8 encode_utf8);
+
+# Блокировка для предотвращения параллельных запусков
+open(SELF, "<", $0) or die "Cannot open $0 - $!";
+flock(SELF, LOCK_EX | LOCK_NB) or exit 1;
+
+# Настройка API - аутентификация через параметры
+my $api_base = $config_ref{api_base} || 'http://localhost/api.php';
+my $api_key  = $config_ref{api_key}  || die "Ошибка: не настроен параметр api_key в конфигурации\n";
+my $api_login = $config_ref{api_login} || die "Ошибка: не настроен параметр api_login в конфигурации\n";
+
+# Базовые параметры аутентификации для всех запросов
+my $auth_params = "api_key=" . uri_escape($api_key) . "&login=" . uri_escape($api_login);
+
+# Инициализация HTTP-клиента
+my $ua = LWP::UserAgent->new(
+    timeout => 30,
+    agent   => 'WikiSync/1.0'
+);
+
+# === Получение пути к вики из таблицы config (id=61) ===
+my $config_record = api_call('GET', "$api_base?get_table_record&table=config&id=61&$auth_params");
+if (!$config_record || $config_record->{error}) {
+    die "Ошибка: не удалось получить путь к вики из таблицы config (id=61)\n";
+}
+
+my $wiki_path = $config_record->{value};
+if (!$wiki_path || !-d $wiki_path) {
+    die "Ошибка: путь к вики не настроен или директория не существует: $wiki_path\n";
+}
+
+print "Путь к вики: $wiki_path\n\n";
+
+# Поиск подходящих файлов
+my %content;
+find(\&wanted, $wiki_path);
+
+my $updated = 0;
+my $errors  = 0;
+
+foreach my $fname (sort keys %content) {
+    # Чтение файла
+    open(my $fh, '<:encoding(UTF-8)', $content{$fname}) or do {
+        warn "Не удалось открыть файл $content{$fname}: $!\n";
+        $errors++;
+        next;
+    };
+    my @lines = <$fh>;
+    close($fh);
+    chomp(@lines);
+
+    # Извлечение IP из метаданных
+    my $ip;
+    foreach my $line (@lines) {
+        if ($line =~ /\%META\:FIELD\{name="DeviceIP"/) {
+            if ($line =~ /value="([0-9]{1,3}(?:\.[0-9]{1,3}){3})"/) {
+                $ip = $1;
+                last;
+            }
+        }
+    }
+
+    next unless $ip && is_valid_ipv4($ip);
+
+    # Получение записи из БД через API
+    my $auth = api_call('GET', "$api_base/user_auth.php?get=user_auth&ip=" . uri_escape($ip) . "&$auth_params");
+    if (!$auth || $auth->{error}) {
+        print "Запись не найдена для IP $ip (файл: $fname)\n";
+        next;
+    }
+
+    # Обновление поля WikiName через метод обновления user_auth
+    my $update_data = {
+        wiki_name => $fname  # Используем нижний регистр как принято в БД
+    };
+
+    my $json_data = encode_json($update_data);
+    my $update_url = "$api_base/user_auth.php?send=update_user_auth&id=$auth->{id}&$auth_params";
+    
+    my $update_req = HTTP::Request->new(POST => $update_url);
+    $update_req->header('Content-Type' => 'application/json');
+    $update_req->content($json_data);
+    
+    my $update_res = $ua->request($update_req);
+    
+    if ($update_res->is_success) {
+        my $result = decode_json(decode_utf8($update_res->decoded_content));
+        if (!$result->{error}) {
+            print "Обновлено: id=$auth->{id} IP=$ip => WikiName=$fname\n";
+            $updated++;
+        } else {
+            warn "Ошибка обновления id=$auth->{id}: $result->{error}\n";
+            $errors++;
+        }
+    } else {
+        warn "HTTP ошибка при обновлении id=$auth->{id}: " . $update_res->status_line . "\n";
+        $errors++;
+    }
+}
+
+print "\n=== ИТОГИ ===\n";
+print "Обработано файлов: " . scalar(keys %content) . "\n";
+print "Успешно обновлено записей: $updated\n";
+print "Ошибок: $errors\n";
+print "Синхронизация завершена.\n";
+
+exit 0;

+ 276 - 0
scripts/utils/wiki/update_wiki_eye.pl

@@ -0,0 +1,276 @@
+#!/usr/bin/perl
+#
+# Синхронизация метаданных устройств в Wiki через API
+#
+# Запуск: perl /path/to/update_wiki_eye.pl
+#
+
+use utf8;
+use warnings;
+use Encode;
+use open qw(:std :encoding(UTF-8));
+no warnings 'utf8';
+
+use strict;
+use English;
+use FindBin '$Bin';
+use lib "$Bin/";
+use eyelib::config;
+use Data::Dumper;
+use File::Find;
+use File::Basename;
+use Fcntl qw(:flock);
+use LWP::UserAgent;
+use HTTP::Request;
+use JSON::XS;
+use URI::Escape;
+use Encode qw(decode_utf8 encode_utf8);
+
+# Блокировка для предотвращения параллельных запусков
+open(SELF, "<", $0) or die "Cannot open $0 - $!";
+flock(SELF, LOCK_EX | LOCK_NB) or exit 1;
+
+# Настройка API - аутентификация через параметры
+my $api_base = $config_ref{api_base} || 'http://localhost/api.php';
+my $api_key  = $config_ref{api_key}  || die "Ошибка: не настроен параметр api_key в конфигурации\n";
+my $api_login = $config_ref{api_login} || die "Ошибка: не настроен параметр api_login в конфигурации\n";
+
+# Инициализация HTTP-клиента
+my $ua = LWP::UserAgent->new(
+    timeout => 60,
+    agent   => 'EyeWikiUpdater/1.0'
+);
+
+# Базовые параметры аутентификации для всех запросов
+my $auth_params = "api_key=" . uri_escape($api_key) . "&login=" . uri_escape($api_login);
+
+# === Получение пути к вики из таблицы config (id=61) ===
+my $config_record = api_call('GET', "$api_base?get_table_record&table=config&id=61&$auth_params");
+if (!$config_record || $config_record->{error}) {
+    die "Ошибка: не удалось получить путь к вики из таблицы config (id=61)\n";
+}
+
+my $wiki_path = $config_record->{value};
+if (!$wiki_path || !-d $wiki_path) {
+    die "Ошибка: путь к вики не настроен или директория не существует: $wiki_path\n";
+}
+
+print "Путь к вики: $wiki_path\n\n";
+
+# Поиск подходящих файлов
+my %content;
+find(\&wanted, $wiki_path);
+
+my $processed = 0;
+my $errors    = 0;
+
+foreach my $fname (sort keys %content) {
+    # Чтение файла
+    open(my $fh, '<:encoding(UTF-8)', $content{$fname}) or do {
+        warn "Не удалось открыть файл $content{$fname}: $!\n";
+        $errors++;
+        next;
+    };
+    my @lines = <$fh>;
+    close($fh);
+    chomp(@lines);
+
+    # Извлечение IP из метаданных
+    my $ip;
+    foreach my $line (@lines) {
+        if ($line =~ /\%META\:FIELD\{name="DeviceIP"/) {
+            if ($line =~ /value="([0-9]{1,3}(?:\.[0-9]{1,3}){3})"/) {
+                $ip = $1;
+                last;
+            }
+        }
+    }
+
+    next unless $ip && is_valid_ipv4($ip);
+
+    # Получение записи из user_auth по IP через API
+    my $auth = api_call('GET', "$api_base/user_auth.php?get=user_auth&ip=" . uri_escape($ip) . "&$auth_params");
+    if (!$auth || $auth->{error} || !$auth->{WikiName}) {
+        next;
+    }
+
+    # Пропускаем шлюзы
+    next if $auth->{WikiName} =~ /^Gateway/;
+
+    print "Found: $auth->{ip} $auth->{mac} ";
+
+    my ($device_name, $device_port);
+    my $error_msg;
+
+    # Определение типа устройства и получение родительских данных
+    eval {
+        if ($auth->{WikiName} =~ /^(Switch|Router)/) {
+            # Для коммутаторов/маршрутизаторов: получаем данные через цепочку устройств
+            my $device = api_call('GET', "$api_base/user_auth.php?get_table_list&table=devices&filter=" . 
+                uri_escape(encode_json({ ip => $ip })) . "&$auth_params");
+            
+            die "Unknown device" unless $device && ref($device) eq 'ARRAY' && @{$device} > 0;
+            $device = $device->[0];
+            
+            # Получаем аплинк-порт
+            my $uplink_ports = api_call('GET', "$api_base/user_auth.php?get_table_list&table=device_ports&filter=" . 
+                uri_escape(encode_json({ device_id => $device->{id}, uplink => 1 })) . "&$auth_params");
+            
+            die "Unknown connection" unless $uplink_ports && ref($uplink_ports) eq 'ARRAY' && @{$uplink_ports} > 0;
+            my $parent_connect = $uplink_ports->[0];
+            
+            # Получаем целевой порт
+            my $parent_port = api_call('GET', "$api_base/user_auth.php?get_table_record&table=device_ports&id=" . 
+                $parent_connect->{target_port_id} . "&$auth_params");
+            
+            die "Unknown port connection" unless $parent_port;
+            
+            # Получаем родительское устройство
+            my $device_parent = api_call('GET', "$api_base/user_auth.php?get_table_record&table=devices&id=" . 
+                $parent_port->{device_id} . "&$auth_params");
+            
+            die "Unknown parent device" unless $device_parent;
+            
+            # Получаем авторизацию родительского устройства
+            my $auth_parent = api_call('GET', "$api_base/user_auth.php?get=user_auth&ip=" . 
+                uri_escape($device_parent->{ip}) . "&$auth_params");
+            
+            die "Unknown auth for device" unless $auth_parent && $auth_parent->{WikiName};
+            
+            $device_name  = $auth_parent->{WikiName};
+            $device_port  = $parent_port->{port};
+        }
+        else {
+            # Для других устройств: получаем данные через соединения
+            my $connections = api_call('GET', "$api_base/user_auth.php?get_table_list&table=connections&filter=" . 
+                uri_escape(encode_json({ auth_id => $auth->{id} })) . "&$auth_params");
+            
+            die "Unknown connection" unless $connections && ref($connections) eq 'ARRAY' && @{$connections} > 0;
+            my $conn = $connections->[0];
+            
+            # Получаем порт устройства
+            my $device_port_rec = api_call('GET', "$api_base/user_auth.php?get_table_record&table=device_ports&id=" . 
+                $conn->{port_id} . "&$auth_params");
+            
+            die "Unknown device port" unless $device_port_rec;
+            
+            # Получаем устройство
+            my $device = api_call('GET', "$api_base/user_auth.php?get_table_record&table=devices&id=" . 
+                $device_port_rec->{device_id} . "&$auth_params");
+            
+            die "Unknown device" unless $device && $device->{user_id};
+            
+            # Получаем авторизацию устройства по user_id и IP
+            my $device_auth_list = api_call('GET', "$api_base/user_auth.php?get_table_list&table=user_auth&filter=" . 
+                uri_escape(encode_json({ user_id => $device->{user_id}, ip => $device->{ip} })) . "&$auth_params");
+            
+            die "Unknown device auth" unless $device_auth_list && ref($device_auth_list) eq 'ARRAY' && @{$device_auth_list} > 0;
+            my $device_auth = $device_auth_list->[0];
+            
+            die "Device auth has no WikiName" unless $device_auth->{WikiName};
+            
+            $device_name = $device_auth->{WikiName};
+            $device_port = $device_port_rec->{port};
+        }
+    };
+    
+    if ($@) {
+        $error_msg = $@;
+        chomp($error_msg);
+        print "Error: $error_msg\n";
+        next;
+    }
+
+    # Подготовка обновленного содержимого файла
+    my @wiki_dev;
+    my %empty_fields = (parent => 1, parent_port => 1, mac => 1);
+
+    foreach my $line (@lines) {
+        if ($line =~ /\%META\:FIELD\{name="Parent"/) {
+            $empty_fields{parent} = 0;
+            if ($device_name) {
+                push(@wiki_dev, '%META:FIELD{name="Parent" title="Parent" value="' . $device_name . '"}%');
+                next;
+            }
+        }
+        elsif ($line =~ /\%META\:FIELD\{name="ParentPort"/) {
+            $empty_fields{parent_port} = 0;
+            if ($device_port) {
+                push(@wiki_dev, '%META:FIELD{name="ParentPort" title="Parent Port" value="' . $device_port . '"}%');
+                next;
+            }
+        }
+        elsif ($line =~ /\%META\:FIELD\{name="Mac"/) {
+            $empty_fields{mac} = 0;
+            if ($auth->{mac}) {
+                push(@wiki_dev, '%META:FIELD{name="Mac" title="Mac" value="' . $auth->{mac} . '"}%');
+                next;
+            }
+        }
+        push(@wiki_dev, $line);
+    }
+
+    # Добавление отсутствующих полей
+    if ($empty_fields{parent} && $device_name) {
+        push(@wiki_dev, '%META:FIELD{name="Parent" title="Parent" value="' . $device_name . '"}%');
+    }
+    if ($empty_fields{parent_port} && $device_port) {
+        push(@wiki_dev, '%META:FIELD{name="ParentPort" title="Parent Port" value="' . $device_port . '"}%');
+    }
+    if ($empty_fields{mac} && $auth->{mac}) {
+        push(@wiki_dev, '%META:FIELD{name="Mac" title="Mac" value="' . $auth->{mac} . '"}%');
+    }
+
+    $device_name  ||= 'None';
+    $device_port  ||= 'None';
+    print "at $device_name $device_port\n";
+
+    # Запись обновленного файла
+    open(my $out_fh, '>:encoding(UTF-8)', $content{$fname}) or do {
+        warn "Ошибка записи файла $content{$fname}: $!\n";
+        $errors++;
+        next;
+    };
+    print $out_fh $_ . "\n" for @wiki_dev;
+    close($out_fh);
+
+    $processed++;
+}
+
+print "\n=== ИТОГИ ===\n";
+print "Обработано файлов: $processed\n";
+print "Ошибок: $errors\n";
+print "Синхронизация завершена.\n";
+
+exit 0;
+
+# === Вспомогательные функции ===
+
+sub api_call {
+    my ($method, $url) = @_;
+    
+    my $req = HTTP::Request->new($method => $url);
+    my $res = $ua->request($req);
+    
+    return undef unless $res->is_success;
+    
+    my $content = decode_utf8($res->decoded_content);
+    return decode_json($content);
+}
+
+sub is_valid_ipv4 {
+    my ($ip) = @_;
+    return $ip =~ /^([0-9]{1,3}\.){3}[0-9]{1,3}$/ && 
+           !grep { $_ > 255 } split(/\./, $ip);
+}
+
+sub wanted {
+    my $filename = $File::Find::name;
+    my $dev_name = basename($filename);
+    
+    if ($dev_name =~ /\.txt$/ && $dev_name =~ /^(Device|Switch|Ups|Sensor|Gateway|Router|Server|Bras)/) {
+        $dev_name =~ s/\.txt$//;
+        $content{$dev_name} = $filename;
+    }
+    return;
+}