update_eye_wiki.pl 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #!/usr/bin/perl
  2. #
  3. # Синхронизация записей пользователей с файлами документации Wiki
  4. # через вызовы к PHP API (без прямого доступа к БД)
  5. #
  6. # Запуск: perl /path/to/update_eye_wiki.pl
  7. #
  8. use utf8;
  9. use warnings;
  10. use Encode;
  11. use open qw(:std :encoding(UTF-8));
  12. no warnings 'utf8';
  13. use strict;
  14. use English;
  15. use FindBin '$Bin';
  16. use lib "/opt/Eye/scripts";
  17. use eyelib::config;
  18. use File::Find;
  19. use File::Basename;
  20. use Fcntl qw(:flock);
  21. use LWP::UserAgent;
  22. use HTTP::Request;
  23. use JSON::XS;
  24. use URI::Escape;
  25. use Encode qw(decode_utf8 encode_utf8);
  26. # Блокировка для предотвращения параллельных запусков
  27. open(SELF, "<", $0) or die "Cannot open $0 - $!";
  28. flock(SELF, LOCK_EX | LOCK_NB) or exit 1;
  29. # Настройка API - аутентификация через параметры
  30. my $api_base = $config_ref{api_base} || 'http://localhost/api.php';
  31. my $api_key = $config_ref{api_key} || die "Ошибка: не настроен параметр api_key в конфигурации\n";
  32. my $api_login = $config_ref{api_login} || die "Ошибка: не настроен параметр api_login в конфигурации\n";
  33. # Базовые параметры аутентификации для всех запросов
  34. my $auth_params = "api_key=" . uri_escape($api_key) . "&api_login=" . uri_escape($api_login);
  35. # Инициализация HTTP-клиента
  36. my $ua = LWP::UserAgent->new(
  37. timeout => 30,
  38. agent => 'EyeWikiSync/1.0'
  39. );
  40. my $api_request_url = $api_base."?".$auth_params;
  41. # берём по коду параметра, а не id записи!
  42. my $option_filter = encode_json({ option_id => '61' });
  43. # === Получение пути к вики из таблицы config (id=61) ===
  44. my $api_request = $api_request_url."&get=table_record&table=config&filter=".uri_escape($option_filter);
  45. my $config_response = api_call($ua, 'GET', $api_request);
  46. if ($config_response->{error}) {
  47. die "Ошибка: не удалось получить путь к вики из таблицы config (id=61): " . $config_response->{error} . "\n";
  48. }
  49. my $wiki_path = $config_response->{data}->{value};
  50. if (!$wiki_path || !-d $wiki_path) {
  51. die "Ошибка: путь к вики не настроен или директория не существует: $wiki_path\n";
  52. }
  53. print "Путь к вики: $wiki_path\n\n";
  54. # Поиск подходящих файлов
  55. my %content;
  56. find(\&wanted, $wiki_path);
  57. my $updated = 0;
  58. my $errors = 0;
  59. foreach my $fname (sort keys %content) {
  60. # Чтение файла
  61. open(my $fh, '<:encoding(UTF-8)', $content{$fname}) or do {
  62. warn "Не удалось открыть файл $content{$fname}: $!\n";
  63. $errors++;
  64. next;
  65. };
  66. my @lines = <$fh>;
  67. close($fh);
  68. chomp(@lines);
  69. # Извлечение IP из метаданных
  70. my $ip;
  71. foreach my $line (@lines) {
  72. if ($line =~ /\%META\:FIELD\{name="DeviceIP"/) {
  73. if ($line =~ /value="([0-9]{1,3}(?:\.[0-9]{1,3}){3})"/) {
  74. $ip = $1;
  75. last;
  76. }
  77. }
  78. }
  79. next unless $ip && is_valid_ipv4($ip);
  80. # Получение записи из БД через API
  81. my $auth_response = api_call($ua, 'GET', $api_request_url. "&get=user_auth&ip=" . uri_escape($ip));
  82. if ($auth_response->{error}) {
  83. print "Запись не найдена для IP $ip (файл: $fname): " . $auth_response->{error} . "\n";
  84. next;
  85. }
  86. my $auth = $auth_response->{data};
  87. # Обновление поля WikiName через метод обновления user_auth
  88. my $update_data = {
  89. wikiname => $fname
  90. };
  91. my $json_data = encode_json($update_data);
  92. my $update_url = $api_request_url. "&send=update_user_auth&id=$auth->{id}";
  93. my $update_req = HTTP::Request->new(POST => $update_url);
  94. $update_req->header('Content-Type' => 'application/json');
  95. $update_req->content($json_data);
  96. my $update_response = api_call($ua, 'POST', $update_url, $json_data);
  97. if ($update_response->{error}) {
  98. warn "Ошибка обновления id=$auth->{id}: " . $update_response->{error} . "\n";
  99. $errors++;
  100. } else {
  101. print "Обновлено: id=$auth->{id} IP=$ip => WikiName=$fname\n";
  102. $updated++;
  103. }
  104. }
  105. print "\n=== ИТОГИ ===\n";
  106. print "Обработано файлов: " . scalar(keys %content) . "\n";
  107. print "Успешно обновлено записей: $updated\n";
  108. print "Ошибок: $errors\n";
  109. print "Синхронизация завершена.\n";
  110. exit 0;
  111. sub api_call {
  112. my ($ua, $method, $url, $content) = @_;
  113. my $req = HTTP::Request->new($method => $url);
  114. if ($content) {
  115. $req->header('Content-Type' => 'application/json');
  116. $req->content($content);
  117. }
  118. my $res = $ua->request($req);
  119. my $result = {};
  120. if (!$res->is_success) {
  121. $result->{error} = $res->status_line;
  122. return $result;
  123. }
  124. eval {
  125. $result->{data} = decode_json($res->decoded_content);
  126. };
  127. if ($@) {
  128. $result->{error} = "JSON parse error: $@";
  129. }
  130. return $result;
  131. }
  132. sub wanted {
  133. my $filename = $File::Find::name;
  134. my $dev_name = basename($filename);
  135. if ($dev_name =~ /\.txt$/ && $dev_name =~ /^(Device|Switch|Ups|Sensor|Gateway|Router|Server|Bras)/) {
  136. $dev_name =~ s/\.txt$//;
  137. $content{$dev_name} = $filename;
  138. }
  139. return;
  140. }
  141. sub is_valid_ipv4 {
  142. my ($ip) = @_;
  143. return $ip =~ /^([0-9]{1,3}\.){3}[0-9]{1,3}$/ && !grep { $_ > 255 } split(/\./, $ip);
  144. }