disableCsrfValidation(); } protected function checkInput() { return true; } protected function checkPermissions() { return CWebUser::getType() == USER_TYPE_SUPER_ADMIN; } protected function doAction() { $mediatypeid = (string) ($_REQUEST['mediatypeid'] ?? ''); try { // Strategy apply buttons are submitted as name="apply_strategy" value=. if (isset($_REQUEST['apply_strategy'])) { $this->applyStrategy($mediatypeid, (string) $_REQUEST['apply_strategy']); } else { $op = (string) ($_REQUEST['op'] ?? ''); switch ($op) { case 'save_alert': $this->saveAlert($mediatypeid); break; case 'save_triggers': $this->saveTriggers(); break; case 'save_class_sev': $this->saveClassSeverity(); break; case 'save_robot': $this->saveRobot($mediatypeid); break; case 'test_robot': $this->testRobot($mediatypeid); break; case 'import_template': $this->importTemplate(); break; case 'save_map': $this->saveMap(); break; case 'apply_switch': $this->applySwitch(); break; case 'init_env': $this->initEnv(); break; case 'init_l2': $this->initL2(); break; case 'save_l2': $this->saveL2(); break; default: error(I18n::t('msg.unknown_op')); } } } catch (Exception $e) { error($e->getMessage()); } $url = (new CUrl('zabbix.php'))->setArgument('action', 'wecom.center.view'); if ($mediatypeid !== '') { $url->setArgument('mediatypeid', $mediatypeid); } $tab = (string) ($_REQUEST['tab'] ?? ''); if (in_array($tab, ['alert', 'opt', 'maint', 'l2'], true)) { $url->setArgument('tab', $tab); if ($tab === 'maint') { $ts = (string) ($_REQUEST['trig_search'] ?? ''); if ($ts !== '') { $url->setArgument('trig_search', $ts); } } } $this->setResponse(new CControllerResponseRedirect($url)); } private function applyStrategy(string $mediatypeid, string $key): void { if (!isset(Helper::STRATEGY_PRESETS[$key])) { throw new Exception(I18n::t('msg.bad_scheme')); } $tid = Helper::templateHostid(); if ($tid === null) { throw new Exception(I18n::t('msg.no_template_host')); } $preset = Helper::STRATEGY_PRESETS[$key]; // 1) threshold on the media (global "how severe still notifies") if ($mediatypeid !== '') { $user = Helper::ensureRobotUser('wecomrobot'); $mask = Helper::severityMaskFromMin((int) $preset['min']); Helper::applyMedia($user['userid'], $mediatypeid, $mask, 0); } // 2) classify + write severities $res = Helper::applyStrategy($tid, $preset['targets']); Helper::saveStrategyKey($key); info(I18n::t('msg.strategy_applied', [ I18n::t($preset['label']), $res['trigger'], $res['prototype'], $res['discovered'] ])); } private function saveAlert(string $mediatypeid): void { $active = isset($_REQUEST['enabled']) ? 0 : 1; // media: 0=enabled, 1=disabled $indices = []; for ($i = 1; $i <= 6; $i++) { if (isset($_REQUEST['sev_'.$i])) { $indices[] = $i; } } $mask = Helper::severityMask($indices); if ($mask === 0 && $active === 0) { $mask = 2; } $user = Helper::ensureRobotUser('wecomrobot'); Helper::applyMedia($user['userid'], $mediatypeid, $mask, $active); info(I18n::t('msg.saved')); } private function saveRobot(string $mediatypeid): void { $url = trim((string) ($_REQUEST['webhook_url'] ?? '')); if ($url !== '') { Helper::setWebhookKey($url); } $user = Helper::ensureRobotUser('wecomrobot'); $cur = Helper::currentMedia($user, $mediatypeid); $mask = $cur['severity'] ?? 48; $active = $cur['active'] ?? 0; Helper::applyMedia($user['userid'], $mediatypeid, $mask, $active); info(I18n::t('msg.robot_saved')); } private function testRobot(string $mediatypeid): void { $mt = Helper::getMediatype($mediatypeid); if (!$mt) { throw new Exception(I18n::t('msg.no_mediatype')); } $url = Helper::webhookUrl($mt); if ($url === '') { throw new Exception(I18n::t('msg.no_url')); } $text = '['.I18n::t('page.title').'] Test OK - '.date('Y-m-d H:i:s'); $res = Helper::testWebhook($url, $text); $body = (string) $res['body']; $ok = (strpos($body, '"errcode":0') !== false) || (strpos($body, '"errcode": 0') !== false); if ($ok) { info(I18n::t('msg.tested')); } else { error(I18n::t('msg.test_failed', [$res['status'], $body])); } } private function importTemplate(): void { $file = (string) ($_REQUEST['template_file'] ?? ''); if ($file === '') { throw new Exception(I18n::t('msg.no_template')); } $module_dir = dirname(__DIR__); $res = Helper::importTemplate($module_dir, $file); $n = isset($res['templates']) && is_array($res['templates']) ? count($res['templates']) : 0; info(I18n::t('msg.imported', [$n])); } private function saveTriggers(): void { $ids = (string) ($_REQUEST['trig_ids'] ?? ''); $ids = array_filter(array_map('trim', explode(',', $ids))); if (!$ids) { throw new Exception(I18n::t('msg.no_triggers')); } $changed = 0; $sev_changed = 0; foreach ($ids as $id) { $id = (int) $id; if ($id <= 0) { continue; } $status = isset($_REQUEST['trig_'.$id]) ? 0 : 1; // checkbox = enabled $priority = null; if (isset($_REQUEST['pse_'.$id]) && $_REQUEST['pse_'.$id] !== '') { $priority = (int) $_REQUEST['pse_'.$id]; $sev_changed++; } Helper::setTriggerState((string) $id, $priority, $status); $changed++; } info(I18n::t('msg.triggers_saved', [$changed])); } private function saveClassSeverity(): void { $ck = (string) ($_REQUEST['class_key'] ?? ''); $p = (int) ($_REQUEST['class_priority'] ?? -1); if ($ck === '' || $p < 0 || $p > 5) { throw new Exception(I18n::t('msg.bad_class_sev')); } $r = Helper::setClassSeverity($ck, $p); info(I18n::t('msg.class_sev_saved', [$r['trigger'] + $r['prototype'], $r['discovered']])); } private function saveMap(): void { $hostid = (string) ($_REQUEST['hostid'] ?? ''); $lat = trim((string) ($_REQUEST['lat'] ?? '')); $lon = trim((string) ($_REQUEST['lon'] ?? '')); if ($hostid === '') { throw new Exception(I18n::t('msg.no_host')); } Helper::setHostLocation($hostid, $lat, $lon); info(I18n::t('msg.map_saved')); } private function applySwitch(): void { $src = (string) ($_REQUEST['switch_src'] ?? ''); $dst = (string) ($_REQUEST['switch_dst'] ?? ''); if ($src === '' || $dst === '') { throw new Exception(I18n::t('msg.need_both')); } if ($src === $dst) { throw new Exception(I18n::t('msg.same_tpl')); } $n = Helper::switchApply($src, $dst); info(I18n::t('msg.switch_applied', [$n])); } private function initEnv(): void { $key = trim((string) ($_REQUEST['key'] ?? $_REQUEST['webhook_url'] ?? '')); $proto = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = (string) ($_SERVER['HTTP_HOST'] ?? ''); $zbxUrl = $host !== '' ? $proto.'://'.$host : ''; $r = Helper::initializeEnvironment($key, $zbxUrl); $parts = []; $parts[] = $r['created_mediatype'] ? I18n::t('msg.init_mediatype_new') : I18n::t('msg.init_mediatype_ok'); $parts[] = $r['created_user'] ? I18n::t('msg.init_user_new') : I18n::t('msg.init_user_ok'); $parts[] = $r['created_action'] ? I18n::t('msg.init_action_new') : I18n::t('msg.init_action_ok'); if ($r['key_set']) { $parts[] = I18n::t('msg.init_key_set'); } // A fresh install should also get the L2 storage/config ready. This is // storage-only and never touches switch relationships or existing config. $l2 = L2Store::initialize(); $parts[] = $l2['config_created'] ? I18n::t('msg.init_l2_new') : I18n::t('msg.init_l2_ok'); info(I18n::t('msg.init_done', [implode(' · ', $parts)])); } private function initL2(): void { $r = L2Store::initialize(); info(I18n::t('msg.l2_init_done', [ $r['config_created'] ? I18n::t('msg.l2_init_config_new') : I18n::t('msg.l2_init_config_ok'), $r['history_rows'] ])); } private function saveL2(): void { $cfg = L2Store::getConfig(); // templates (multi-select) $tpls = $_REQUEST['templates'] ?? []; if (is_array($tpls)) { $tpls = array_values(array_filter(array_map('strval', $tpls), function ($s) { return trim($s) !== ''; })); $cfg['templates'] = $tpls; } $cfg['refresh_seconds'] = max(30, (int) ($_REQUEST['refresh_seconds'] ?? $cfg['refresh_seconds'])); // One interval drives both the full collection and the history snapshot: // every successful full collection writes exactly one historical row. $cfg['full_interval_min'] = max(5, min(1440, (int) ($_REQUEST['full_interval_min'] ?? $cfg['full_interval_min']))); $cfg['alarm_interval_min'] = max(1, (int) ($_REQUEST['alarm_interval_min'] ?? $cfg['alarm_interval_min'])); $cfg['history_interval_min'] = $cfg['full_interval_min']; // legacy compatibility $cfg['history_days'] = max(1, min(30, (int) ($_REQUEST['history_days'] ?? $cfg['history_days']))); $cfg['root_label'] = trim((string) ($_REQUEST['root_label'] ?? $cfg['root_label'])); if ($cfg['root_label'] === '') { $cfg['root_label'] = 'COBCO'; } $cfg['other_label'] = trim((string) ($_REQUEST['other_label'] ?? $cfg['other_label'])); if ($cfg['other_label'] === '') { $cfg['other_label'] = 'Other'; } // Networks: one line per network, pipe separated: // KEY | display label | keyword1,keyword2 | #RRGGBB $netText = trim((string) ($_REQUEST['networks_text'] ?? '')); if ($netText !== '') { $networks = []; foreach (preg_split('/\R/u', $netText) as $line) { $line = trim($line); if ($line === '') { continue; } $p = array_map('trim', explode('|', $line)); $key = strtoupper((string) ($p[0] ?? '')); $label = (string) ($p[1] ?? $key); if ($key === '' || $label === '') { continue; } $kws = array_values(array_filter(array_map('trim', preg_split('/[,,;;\s]+/u', (string) ($p[2] ?? $key))), 'strlen')); $color = (string) ($p[3] ?? '#607D8B'); if (!preg_match('/^#[0-9a-fA-F]{6}$/', $color)) { $color = '#607D8B'; } $networks[] = ['key' => $key, 'label' => $label, 'keywords' => $kws, 'color' => $color]; } if ($networks) { $cfg['networks'] = $networks; } } // Core hierarchy: optional upstream roles (empty pattern = hidden) and // the sub-core definitions (monitoring / office / future production). if (isset($_REQUEST['core_hierarchy_present'])) { $netKeys = array_map(function ($n) { return (string) ($n['key'] ?? ''); }, (array) $cfg['networks']); $hier = []; foreach (['firewall', 'behavior', 'total_core'] as $role) { $match = (string) ($_REQUEST['hier_'.$role.'_match'] ?? 'contains'); if ($match !== 'regex') { $match = 'contains'; } $hier[$role] = [ 'match' => $match, 'pattern' => trim((string) ($_REQUEST['hier_'.$role.'_pattern'] ?? '')) ]; } $subNet = (array) ($_REQUEST['sub_net'] ?? []); $subKey = (array) ($_REQUEST['sub_key'] ?? []); $subLabel = (array) ($_REQUEST['sub_label'] ?? []); $subMatch = (array) ($_REQUEST['sub_match'] ?? []); $subPattern = (array) ($_REQUEST['sub_pattern'] ?? []); $subs = []; $coreRules = []; foreach ($subKey as $i => $key) { $pattern = trim((string) ($subPattern[$i] ?? '')); $net = strtoupper(trim((string) ($subNet[$i] ?? ''))); if ($net !== '' && !in_array($net, $netKeys, true)) { $net = ''; } $match = (string) ($subMatch[$i] ?? 'contains'); if ($match !== 'regex') { $match = 'contains'; } $key = preg_replace('/[^a-z0-9_]/i', '', (string) $key); if ($key === '') { $key = 'sub'.($i + 1); } $label = trim((string) ($subLabel[$i] ?? '')); $subs[] = [ 'key' => $key, 'label' => $label !== '' ? $label : $key, 'net' => $net, 'match' => $match, 'pattern' => $pattern ]; if ($pattern !== '') { $coreRules[] = ['net' => $net, 'match' => $match, 'pattern' => $pattern]; } } $hier['sub_cores'] = $subs; $cfg['core_hierarchy'] = $hier; // Keep the old flat key in sync for collectors/frontends that // still read core_rules. Empty future-core rows are retained in // core_hierarchy as visible placeholder configuration but are not // emitted as active matching rules. $cfg['core_rules'] = $coreRules; } elseif (isset($_REQUEST['core_rules_present'])) { $coreNets = (array) ($_REQUEST['core_net'] ?? []); $coreMatch = (array) ($_REQUEST['core_match'] ?? []); $corePattern = (array) ($_REQUEST['core_pattern'] ?? []); $netKeys = array_map(function ($n) { return (string) ($n['key'] ?? ''); }, (array) $cfg['networks']); $coreRules = []; foreach ($coreNets as $i => $net) { $net = strtoupper(trim((string) $net)); if ($net !== '' && !in_array($net, $netKeys, true)) { $net = ''; } $match = (string) ($coreMatch[$i] ?? 'contains'); if ($match !== 'regex') { $match = 'contains'; } $pattern = trim((string) ($corePattern[$i] ?? '')); if ($pattern === '') { continue; } $coreRules[] = ['net' => $net, 'match' => $match, 'pattern' => $pattern]; } $cfg['core_rules'] = $coreRules; } $cfg['latency_warn'] = (float) ($_REQUEST['latency_warn'] ?? $cfg['latency_warn']); $cfg['latency_bad'] = (float) ($_REQUEST['latency_bad'] ?? $cfg['latency_bad']); $colors = $cfg['colors']; foreach (['100', '1000', '2500', '10000', '20000', 'unknown', 'down'] as $k) { $v = trim((string) ($_REQUEST['color_'.$k] ?? '')); if ($v !== '' && preg_match('/^#[0-9a-fA-F]{6}$/', $v)) { $colors[$k] = $v; } } $cfg['colors'] = $colors; // grouping rules (parallel arrays) $rm = $_REQUEST['rule_match'] ?? []; $rp = $_REQUEST['rule_pattern'] ?? []; $rl = $_REQUEST['rule_label'] ?? []; $rb = $_REQUEST['rule_big'] ?? []; $rules = []; if (is_array($rm)) { $n = count($rm); for ($i = 0; $i < $n; $i++) { $pat = trim((string) ($rp[$i] ?? '')); $lab = trim((string) ($rl[$i] ?? '')); if ($pat === '' || $lab === '') { continue; } $rules[] = [ 'match' => ((string) ($rm[$i] ?? 'contains') === 'regex') ? 'regex' : 'contains', 'pattern' => $pat, 'label' => $lab, 'big' => !empty($rb[$i]) ]; } } $cfg['group_rules'] = $rules; L2Store::saveConfig($cfg); info(I18n::t('msg.l2_saved')); } }