From 2463715e3294477ff2989c71d94807402b891768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AC=A2?= <2711452309@qq.com> Date: Fri, 12 Jun 2026 10:19:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E7=8B=AC=E7=AB=8BIPv4=20?= =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=85=BC=E5=AE=B9=E9=98=B2=E7=81=AB=E5=A2=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mofang/clicd.php | 375 ++++++++++++++++++++++-- Mofang/clicd.zip | Bin 23833 -> 0 bytes Mofang/handlers/webssh.php | 2 +- Mofang/templates/firewall.html | 505 +++++++++++++++++++++++++++++++++ Mofang/templates/info.html | 2 +- Mofang/templates/nat.html | 2 +- 6 files changed, 855 insertions(+), 31 deletions(-) delete mode 100644 Mofang/clicd.zip create mode 100644 Mofang/templates/firewall.html diff --git a/Mofang/clicd.php b/Mofang/clicd.php index 7313aa5..d7c313a 100644 --- a/Mofang/clicd.php +++ b/Mofang/clicd.php @@ -37,10 +37,10 @@ function clicd_json_response($payload) function clicd_MetaData() { return [ - 'DisplayName' => 'CLICD 对接模块 by 欢-Huan and ChatGPT 5.5', + 'DisplayName' => 'CLICD 对接模块 by 欢-Huan and ChatGPT 5.5 and DeepSeek V4', 'APIVersion' => '1.1', 'HelpDoc' => 'https://github.com/MengMengCode/CLICD', - 'version' => '1.0.1', + 'version' => '1.0.5', ]; } @@ -59,10 +59,19 @@ function clicd_ConfigOptions() ['type' => 'text', 'name' => '入站流量 GB', 'description' => 'in_out 模式下入站流量限制,0 表示不限制', 'default' => '0', 'key' => 'traffic_in_gb'], ['type' => 'text', 'name' => '出站流量 GB', 'description' => 'in_out 模式下出站流量限制,0 表示不限制', 'default' => '0', 'key' => 'traffic_out_gb'], ['type' => 'text', 'name' => 'IO 速度 MB/s', 'description' => '磁盘 IO 限制,0 表示不限制', 'default' => '0', 'key' => 'io_speed_mbps'], + ['type' => 'dropdown', 'name' => '分配 NAT', 'description' => '开通时是否分配 NAT 端口映射', 'default' => 'true', 'key' => 'assign_nat', 'options' => ['true' => '启用', 'false' => '禁用']], ['type' => 'text', 'name' => 'NAT 端口数量', 'description' => '开通时分配的端口映射数量,最小 2', 'default' => '2', 'key' => 'port_mapping_count'], ['type' => 'text', 'name' => '快照配额', 'description' => '每台实例允许保留的快照数量', 'default' => '3', 'key' => 'snapshot_limit'], ['type' => 'text', 'name' => '额外端口', 'description' => '逗号分隔的容器端口,例如 80,443', 'default' => '', 'key' => 'extra_ports'], + ['type' => 'dropdown', 'name' => '自动公网 IPv4', 'description' => '开通时是否从 CLICD 公网 IPv4 池分配独立 IPv4', 'default' => 'false', 'key' => 'assign_ipv4', 'options' => ['true' => '启用', 'false' => '禁用']], + ['type' => 'text', 'name' => '公网 IPv4 数量', 'description' => '自动分配公网 IPv4 的数量,通常填写 1', 'default' => '1', 'key' => 'ipv4_count'], + ['type' => 'text', 'name' => '指定公网 IPv4', 'description' => '指定分配的公网 IPv4,多个用逗号分隔;留空则从地址池自动分配', 'default' => '', 'key' => 'public_ipv4s'], ['type' => 'dropdown', 'name' => '自动 IPv6', 'description' => '开通时自动分配 IPv6', 'default' => 'false', 'key' => 'assign_ipv6', 'options' => ['true' => '启用', 'false' => '禁用']], + ['type' => 'text', 'name' => 'IPv6 数量', 'description' => '自动分配 IPv6 的数量,通常填写 1', 'default' => '1', 'key' => 'ipv6_count'], + ['type' => 'text', 'name' => '指定 IPv6', 'description' => '指定分配的 IPv6 地址,多个用逗号分隔;留空则从地址池自动分配', 'default' => '', 'key' => 'ipv6_addresses'], + ['type' => 'dropdown', 'name' => 'SSH 鉴权模式', 'description' => 'auto_password=自动生成密码,password=使用指定密码,key=使用 SSH 公钥', 'default' => 'auto_password', 'key' => 'ssh_auth_mode', 'options' => ['auto_password' => '自动密码', 'password' => '指定密码', 'key' => 'SSH 公钥']], + ['type' => 'text', 'name' => '指定 SSH 密码', 'description' => 'SSH 鉴权模式为 password 时使用;其他模式留空', 'default' => '', 'key' => 'ssh_password'], + ['type' => 'text', 'name' => 'SSH 公钥', 'description' => 'SSH 鉴权模式为 key 时使用;填写完整 public key', 'default' => '', 'key' => 'ssh_public_key'], ['type' => 'dropdown', 'name' => '同步到期时间', 'description' => '开通/续费时把魔方到期日期同步到 CLICD,格式会转换为 YYYY-MM-DD', 'default' => 'true', 'key' => 'sync_expiry', 'options' => ['true' => '启用', 'false' => '禁用']], ]; } @@ -203,16 +212,101 @@ function clicd_container_name($params) return trim($name, '-.'); } -function clicd_public_host($params, $container = []) +function clicd_host_id($params) +{ + foreach (['hostid', 'id', 'serviceid', 'service_id', 'relid'] as $key) { + if (!empty($params[$key]) && is_numeric($params[$key])) { + return (int)$params[$key]; + } + } + return 0; +} +function clicd_first_string($value) +{ + if (is_array($value)) { + foreach ($value as $item) { + if (is_array($item)) { + foreach (['address', 'ip', 'ipv4', 'public_ip', 'public_ipv4'] as $key) { + if (!empty($item[$key])) { + $itemValue = trim((string)$item[$key]); + if ($itemValue !== '') { + return $itemValue; + } + } + } + continue; + } + + $itemValue = trim((string)$item); + if ($itemValue !== '') { + return $itemValue; + } + } + return ''; + } + + $value = trim((string)$value); + return $value; +} + +function clicd_public_host_from_container($container = []) { if (is_array($container)) { - foreach (['nat_public_ip', 'public_ip', 'host_ip', 'external_ip', 'node_ip', 'nat_host'] as $key) { + foreach (['public_ipv4s', 'public_ipv4', 'public_ip', 'ipv4_addresses', 'ipv4', 'nat_public_ip', 'host_ip', 'external_ip', 'node_ip', 'nat_host'] as $key) { if (!empty($container[$key])) { - return trim((string)$container[$key]); + $value = clicd_first_string($container[$key]); + if ($value !== '') { + return $value; + } } } } + return ''; +} + +function clicd_public_ipv4_from_routing($params, $container = []) +{ + if (!is_array($container)) { + return ''; + } + + $containerId = isset($container['id']) ? (string)$container['id'] : ''; + $containerName = isset($container['name']) ? (string)$container['name'] : clicd_container_name($params); + + $res = clicd_request($params, '/api/v1/routing', [], 'GET', 30); + if (!clicd_success($res) || empty($res['data']['ipv4_assignments']) || !is_array($res['data']['ipv4_assignments'])) { + return ''; + } + + foreach ($res['data']['ipv4_assignments'] as $assignment) { + if (!is_array($assignment)) { + continue; + } + $matchId = $containerId !== '' && isset($assignment['container_id']) && (string)$assignment['container_id'] === $containerId; + $matchName = $containerName !== '' && isset($assignment['container_name']) && (string)$assignment['container_name'] === $containerName; + if ($matchId || $matchName) { + return clicd_first_string($assignment['address'] ?? ''); + } + } + + return ''; +} + +function clicd_public_host($params, $container = [], $useRouting = false) +{ + $fromContainer = clicd_public_host_from_container($container); + if ($fromContainer !== '') { + return $fromContainer; + } + + if ($useRouting) { + $fromRouting = clicd_public_ipv4_from_routing($params, $container); + if ($fromRouting !== '') { + return $fromRouting; + } + } + foreach (['server_ip', 'ip'] as $key) { if (!empty($params[$key])) { $value = trim((string)$params[$key]); @@ -403,6 +497,24 @@ function clicd_extra_ports($value) return array_values(array_unique($ports)); } +function clicd_csv_values($value) +{ + if (is_array($value)) { + $parts = $value; + } else { + $parts = preg_split('/[,;\s]+/', (string)$value); + } + + $result = []; + foreach ($parts as $part) { + $part = trim((string)$part); + if ($part !== '') { + $result[] = $part; + } + } + return array_values(array_unique($result)); +} + function clicd_expiry_from_params($params) { $options = $params['configoptions'] ?? []; @@ -435,6 +547,21 @@ function clicd_container_payload($params) { $options = $params['configoptions'] ?? []; $trafficMode = $options['traffic_mode'] ?? 'total'; + $assignNat = clicd_bool_option($options['assign_nat'] ?? 'true', true); + $assignIpv4 = clicd_bool_option($options['assign_ipv4'] ?? 'false', false); + $assignIpv6 = clicd_bool_option($options['assign_ipv6'] ?? 'false', false); + $publicIpv4s = clicd_csv_values($options['public_ipv4s'] ?? ''); + $ipv6Addresses = clicd_csv_values($options['ipv6_addresses'] ?? ''); + if (!empty($publicIpv4s)) { + $assignIpv4 = true; + } + if (!empty($ipv6Addresses)) { + $assignIpv6 = true; + } + $sshAuthMode = strtolower(trim((string)($options['ssh_auth_mode'] ?? 'auto_password'))); + if (!in_array($sshAuthMode, ['auto_password', 'password', 'key'], true)) { + $sshAuthMode = 'auto_password'; + } return [ 'name' => clicd_container_name($params), @@ -451,9 +578,18 @@ function clicd_container_payload($params) 'traffic_out_gb' => clicd_int_option($options, 'traffic_out_gb', 0), 'io_speed_mbps' => clicd_int_option($options, 'io_speed_mbps', 0), 'extra_ports' => clicd_extra_ports($options['extra_ports'] ?? ''), - 'port_mapping_count' => max(2, clicd_int_option($options, 'port_mapping_count', 2)), + 'port_mapping_count' => $assignNat ? max(2, clicd_int_option($options, 'port_mapping_count', 2)) : 0, + 'assign_nat' => $assignNat, + 'assign_ipv4' => $assignIpv4, + 'ipv4_count' => max(1, clicd_int_option($options, 'ipv4_count', 1)), + 'public_ipv4s' => $publicIpv4s, 'snapshot_limit' => max(1, clicd_int_option($options, 'snapshot_limit', 3)), - 'assign_ipv6' => clicd_bool_option($options['assign_ipv6'] ?? 'false', false), + 'assign_ipv6' => $assignIpv6, + 'ipv6_count' => max(1, clicd_int_option($options, 'ipv6_count', 1)), + 'ipv6_addresses' => $ipv6Addresses, + 'ssh_auth_mode' => $sshAuthMode, + 'ssh_password' => (string)($options['ssh_password'] ?? ''), + 'ssh_public_key' => trim((string)($options['ssh_public_key'] ?? '')), 'expires_at' => clicd_expiry_from_params($params), ]; } @@ -477,6 +613,9 @@ function clicd_request_value($key, $default = '') { if (function_exists('input')) { $value = input('param.' . $key); + if ($value === null) { + $value = input('*.' . $key); + } return $value === null ? $default : $value; } if (isset($_POST[$key])) { @@ -821,16 +960,35 @@ function clicd_info_ajax($params) ]; } +function clicd_domain_status_from_container($container) +{ + if (!is_array($container)) { + return 'Active'; + } + + if (!empty($container['policy_blocked'])) { + return 'Suspended'; + } + + $status = strtolower(trim((string)($container['status'] ?? ''))); + if (in_array($status, ['suspended', 'blocked', 'policy_blocked', 'disabled'], true)) { + return 'Suspended'; + } + + return 'Active'; +} + function clicd_update_host_from_container($params, $container) { - if (empty($params['hostid']) || !is_array($container)) { + $hostId = clicd_host_id($params); + if ($hostId <= 0 || !is_array($container)) { return; } $update = [ - 'domainstatus' => (($container['status'] ?? '') === 'running') ? 'Active' : 'Suspended', + 'domainstatus' => clicd_domain_status_from_container($container), 'username' => 'root', - 'dedicatedip' => clicd_public_host($params, $container), + 'dedicatedip' => clicd_public_host($params, $container, true), ]; $sshPort = clicd_container_ssh_port($container); @@ -844,7 +1002,7 @@ function clicd_update_host_from_container($params, $container) } try { - Db::name('host')->where('id', $params['hostid'])->update($update); + Db::name('host')->where('id', $hostId)->update($update); } catch (\Exception $e) { clicd_debug('host update failed', $e->getMessage()); } @@ -879,21 +1037,24 @@ function clicd_CreateAccount($params) return ['status' => 'error', 'msg' => clicd_message($res, '开通失败')]; } - $detail = clicd_find_container($params); - if (clicd_success($detail) && isset($detail['data'])) { - clicd_update_host_from_container($params, $detail['data']); - } elseif (!empty($params['hostid'])) { + $hostId = clicd_host_id($params); + if ($hostId > 0) { try { - Db::name('host')->where('id', $params['hostid'])->update([ + Db::name('host')->where('id', $hostId)->update([ 'domainstatus' => 'Active', 'username' => 'root', - 'dedicatedip' => clicd_public_host($params), + 'dedicatedip' => clicd_public_ipv4_from_routing($params) ?: clicd_public_host($params), ]); } catch (\Exception $e) { return ['status' => 'error', 'msg' => '开通成功但同步魔方数据库失败: ' . $e->getMessage()]; } } + $detail = clicd_find_container($params); + if (clicd_success($detail) && isset($detail['data'])) { + clicd_update_host_from_container($params, $detail['data']); + } + return ['status' => 'success', 'msg' => clicd_message($res, '开通成功')]; } @@ -1001,9 +1162,10 @@ function clicd_CrackPassword($params, $new_pass) } $password = $res['data']['ssh_password'] ?? $res['data']['password'] ?? $new_pass; - if (!empty($params['hostid'])) { + $hostId = clicd_host_id($params); + if ($hostId > 0) { try { - Db::name('host')->where('id', $params['hostid'])->update(['password' => clicd_store_password($password)]); + Db::name('host')->where('id', $hostId)->update(['password' => clicd_store_password($password)]); $detail = clicd_find_container($params); if (clicd_success($detail) && isset($detail['data'])) { clicd_update_host_from_container($params, $detail['data']); @@ -1262,19 +1424,155 @@ function clicd_webssh($params) ]; } +function clicd_firewallList($params) +{ + $container = []; + $containerId = clicd_container_api_id($params, $container); + $res = clicd_request($params, '/api/v1/containers/' . rawurlencode($containerId) . '/firewall', [], 'GET', 30); + if (!clicd_success($res) || empty($res['data'])) { + return ['status' => 'error', 'msg' => clicd_message($res, '获取防火墙设置失败')]; + } + + return [ + 'status' => 200, + 'msg' => '获取成功', + 'data' => $res['data'], + ]; +} + +function clicd_firewallUpdate($params) +{ + $input = clicd_json_input(); + $enabled = clicd_param_value($input, 'enabled', 'true'); + $enabled = filter_var($enabled, FILTER_VALIDATE_BOOLEAN); + $rules = clicd_param_value($input, 'rules', '[]'); + + if (is_string($rules)) { + $decodedRules = json_decode($rules, true); + if (is_array($decodedRules)) { + $rules = $decodedRules; + } + } + if (!is_array($rules)) { + $rules = []; + } + + $payload = [ + 'enabled' => $enabled, + 'rules' => $rules, + ]; + + $container = []; + $containerId = clicd_container_api_id($params, $container); + $res = clicd_request($params, '/api/v1/containers/' . rawurlencode($containerId) . '/firewall', $payload, 'PUT', 30); + if (!clicd_success($res)) { + return ['status' => 'error', 'msg' => clicd_message($res, '更新防火墙设置失败')]; + } + + // GET after PUT to confirm the actual state after CLICD processes it + $getRes = clicd_request($params, '/api/v1/containers/' . rawurlencode($containerId) . '/firewall', [], 'GET', 30); + $actualData = []; + if (clicd_success($getRes) && !empty($getRes['data']) && is_array($getRes['data'])) { + $actualData = $getRes['data']; + } + + return [ + 'status' => 200, + 'msg' => clicd_message($res, '防火墙设置已更新'), + 'data' => $actualData, + ]; +} + +function clicd_firewall_ajax($params) +{ + $input = clicd_json_input(); + $action = strtolower(trim((string)clicd_param_value($input, 'action', ''))); + $debug = [clicd_debug_entry('Firewall ajax received', [ + 'action' => $action, + 'input' => $input, + 'query' => $_GET, + ])]; + + $container = []; + $containerId = clicd_container_api_id($params, $container); + $debug[] = clicd_debug_entry('Container resolved', [ + 'container_id' => $containerId, + 'container' => [ + 'id' => $container['id'] ?? null, + 'uuid' => $container['uuid'] ?? null, + 'name' => $container['name'] ?? null, + ], + ]); + + if (!in_array($action, ['list', 'update'], true)) { + return ['status' => 'error', 'msg' => '未知防火墙操作', 'debug' => $debug]; + } + + if ($action === 'list') { + $call = clicd_request_debug($params, '/api/v1/containers/' . rawurlencode($containerId) . '/firewall', [], 'GET', 30); + $debug[] = $call['debug']; + $res = $call['response']; + if (!clicd_success($res) || empty($res['data'])) { + return ['status' => 'error', 'msg' => clicd_message($res, '获取防火墙设置失败'), 'debug' => $debug]; + } + return [ + 'status' => 'success', + 'msg' => '获取成功', + 'data' => $res['data'], + 'debug' => $debug, + ]; + } + + // update + $enabled = clicd_param_value($input, 'enabled', 'true'); + $enabled = filter_var($enabled, FILTER_VALIDATE_BOOLEAN); + $rules = clicd_param_value($input, 'rules', '[]'); + + if (is_string($rules)) { + $decodedRules = json_decode($rules, true); + if (is_array($decodedRules)) { + $rules = $decodedRules; + } + } + if (!is_array($rules)) { + $rules = []; + } + + $payload = [ + 'enabled' => $enabled, + 'rules' => $rules, + ]; + + $call = clicd_request_debug($params, '/api/v1/containers/' . rawurlencode($containerId) . '/firewall', $payload, 'PUT', 30); + $debug[] = $call['debug']; + $res = $call['response']; + + if (!clicd_success($res)) { + return ['status' => 'error', 'msg' => clicd_message($res, '更新防火墙设置失败'), 'debug' => $debug]; + } + + return [ + 'status' => 'success', + 'msg' => clicd_message($res, '防火墙设置已更新'), + 'data' => $res['data'] ?? [], + 'debug' => $debug, + ]; +} + function clicd_AllowFunction() { return [ - 'client' => ['TrafficReset', 'randomPort', 'addNat', 'updateNat', 'deleteNat', 'natList', 'infoData', 'webssh'], - 'admin' => ['TrafficReset', 'randomPort', 'addNat', 'updateNat', 'deleteNat', 'natList', 'infoData', 'webssh'], + 'client' => ['TrafficReset', 'randomPort', 'addNat', 'updateNat', 'deleteNat', 'natList', 'infoData', 'webssh', 'firewallList', 'firewallUpdate'], + 'admin' => ['TrafficReset', 'randomPort', 'addNat', 'updateNat', 'deleteNat', 'natList', 'infoData', 'webssh', 'firewallList', 'firewallUpdate'], ]; } function clicd_ClientArea($params) { return [ - 'info' => ['name' => '实例信息'], - 'nat' => ['name' => 'NAT转发'], + 'info' => ['name' => '实例信息'], + 'nat' => ['name' => 'NAT转发'], + 'firewall' => ['name' => '防火墙'], ]; } @@ -1287,8 +1585,11 @@ function clicd_ClientAreaOutput($params, $key) if ($func === 'infoajax') { clicd_json_response(clicd_info_ajax($params)); } + if ($func === 'firewallajax') { + clicd_json_response(clicd_firewall_ajax($params)); + } - if (!in_array($key, ['info', 'nat'], true)) { + if (!in_array($key, ['info', 'nat', 'firewall'], true)) { return ''; } @@ -1298,6 +1599,7 @@ function clicd_ClientAreaOutput($params, $key) } $c = $res['data']; + $publicHost = clicd_public_host($params, $c, true); if ($key === 'nat') { $operation = clicd_handle_nat_post($params); @@ -1315,8 +1617,8 @@ function clicd_ClientAreaOutput($params, $key) 'container' => $c, 'container_name'=> $c['name'] ?? clicd_container_name($params), 'ssh_port' => $c['ssh_port'] ?? '', - 'server_ip' => $params['server_ip'] ?? parse_url(clicd_base_url($params), PHP_URL_HOST), - 'nat_host' => $params['server_ip'] ?? parse_url(clicd_base_url($params), PHP_URL_HOST), + 'server_ip' => $publicHost, + 'nat_host' => $publicHost, 'operation_msg' => $operationMsg, 'service_id' => clicd_request_value('id', $params['hostid'] ?? ''), 'area_key' => 'nat', @@ -1325,6 +1627,19 @@ function clicd_ClientAreaOutput($params, $key) ]; } + if ($key === 'firewall') { + return [ + 'template' => 'templates/firewall.html', + 'vars' => [ + 'container' => $c, + 'container_name' => $c['name'] ?? clicd_container_name($params), + 'server_ip' => $publicHost, + 'service_id' => clicd_request_value('id', $params['hostid'] ?? ''), + 'area_key' => 'firewall', + ], + ]; + } + $initialRxBytes = (int)($c['traffic_used_rx'] ?? $c['rx_bytes'] ?? 0); $initialTxBytes = (int)($c['traffic_used_tx'] ?? $c['tx_bytes'] ?? 0); $initialTrafficUsed = ($initialRxBytes || $initialTxBytes) ? round(($initialRxBytes + $initialTxBytes) / 1073741824, 2) : '-'; @@ -1342,8 +1657,8 @@ function clicd_ClientAreaOutput($params, $key) 'vars' => [ 'container' => $c, 'status_text' => (($c['status'] ?? '') === 'running') ? '运行中' : '已关机', - 'server_ip' => $params['server_ip'] ?? parse_url(clicd_base_url($params), PHP_URL_HOST), - 'ssh_host' => $params['server_ip'] ?? parse_url(clicd_base_url($params), PHP_URL_HOST), + 'server_ip' => $publicHost, + 'ssh_host' => $publicHost, 'ssh_port' => $c['ssh_port'] ?? '', 'ssh_password' => $c['ssh_password'] ?? '', 'ipv4' => $c['ip'] ?? '', @@ -1366,3 +1681,7 @@ function clicd_ClientAreaOutput($params, $key) ], ]; } + + + + diff --git a/Mofang/clicd.zip b/Mofang/clicd.zip deleted file mode 100644 index 66426cc6270bdef0ba73ab5219b0c273a589ede6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23833 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 A5&!@I diff --git a/Mofang/handlers/webssh.php b/Mofang/handlers/webssh.php index 887308a..3be35c7 100644 --- a/Mofang/handlers/webssh.php +++ b/Mofang/handlers/webssh.php @@ -1,4 +1,4 @@ - +.clicd-fw-panel{font-size:14px;color:#1f2937} +.clicd-fw-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:16px} +.clicd-fw-card{border:1px solid #e5e7eb;border-radius:6px;padding:12px;background:#fff} +.clicd-fw-label{color:#6b7280;font-size:12px;margin-bottom:4px} +.clicd-fw-value{font-size:18px;font-weight:600;word-break:break-all} +.clicd-fw-section{border:1px solid #e5e7eb;border-radius:6px;background:#fff;padding:12px;margin-top:8px} +.clicd-fw-title{font-weight:600;margin:18px 0 8px} +.clicd-fw-muted{color:#6b7280} +.clicd-fw-toggle-row{display:flex;align-items:center;gap:12px;margin-bottom:12px} +.clicd-fw-toggle{position:relative;display:inline-flex;width:48px;height:26px;cursor:pointer} +.clicd-fw-toggle input{opacity:0;width:0;height:0} +.clicd-fw-toggle-slider{position:absolute;inset:0;background:#d1d5db;border-radius:26px;transition:.25s} +.clicd-fw-toggle-slider:before{content:"";position:absolute;width:22px;height:22px;border-radius:50%;background:#fff;top:2px;left:2px;transition:.25s}.clicd-fw-toggle .clicd-fw-toggle-slider:before{width:16px;height:16px;top:2px;left:2px} +.clicd-fw-toggle input:checked+.clicd-fw-toggle-slider{background:#10b981} +.clicd-fw-toggle input:checked+.clicd-fw-toggle-slider:before{transform:translateX(22px)} +.clicd-fw-toggle-label{font-size:14px;font-weight:500} +.clicd-fw-status{font-size:13px;color:#6b7280} +.clicd-fw-rule-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;align-items:end} +.clicd-fw-field label{display:block;color:#6b7280;font-size:12px;margin-bottom:4px} +.clicd-fw-input,.clicd-fw-select{width:100%;height:34px;border:1px solid #d1d5db;border-radius:4px;padding:6px 8px;box-sizing:border-box} +.clicd-fw-input:focus,.clicd-fw-select:focus{border-color:#2563eb;outline:none} +.clicd-fw-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:end} +.clicd-fw-btn{height:34px;border:1px solid #2563eb;background:#2563eb;color:#fff;border-radius:4px;padding:0 12px;cursor:pointer;font-size:13px} +.clicd-fw-btn[disabled]{opacity:.6;cursor:not-allowed} +.clicd-fw-btn-secondary{border-color:#d1d5db;background:#fff;color:#374151} +.clicd-fw-btn-danger{border-color:#dc2626;background:#dc2626;color:#fff} +.clicd-fw-btn-sm{height:30px;padding:0 10px;font-size:12px} +.clicd-fw-rules{display:flex;flex-direction:column;gap:10px;margin-top:8px} +.clicd-fw-rule{border:1px solid #e5e7eb;border-radius:6px;background:#fff;padding:12px} +.clicd-fw-rule-header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;flex-wrap:wrap} +.clicd-fw-rule-direction{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:600} +.clicd-fw-direction-in{background:#dbeafe;color:#1d4ed8} +.clicd-fw-direction-out{background:#fef3c7;color:#92400e} +.clicd-fw-rule-action{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:600} +.clicd-fw-action-ACCEPT{background:#d1fae5;color:#065f46} +.clicd-fw-action-DROP{background:#fee2e2;color:#991b1b} +.clicd-fw-rule-desc{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px} +.clicd-fw-rule-detail{font-size:13px;color:#374151;display:flex;align-items:center;gap:8px;flex-wrap:wrap} +.clicd-fw-rule-detail .sep{color:#d1d5db} +.clicd-fw-rule-edit-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;margin-top:8px;padding-top:8px;border-top:1px solid #e5e7eb} +.clicd-fw-message{border:1px solid #bfdbfe;background:#eff6ff;color:#1d4ed8;border-radius:6px;padding:10px 12px;margin-bottom:12px;display:none} +.clicd-fw-message.error{border-color:#fecaca;background:#fef2f2;color:#b91c1c} +.clicd-fw-debug{margin-top:12px;border:1px dashed #d1d5db;border-radius:6px;background:#f9fafb;padding:10px;color:#374151;white-space:pre-wrap;font-size:12px;display:none} +.clicd-fw-modal-mask{position:fixed;inset:0;background:rgba(15,23,42,.42);display:none;align-items:center;justify-content:center;z-index:9999;padding:16px} +.clicd-fw-modal{width:min(420px,100%);background:#fff;border-radius:6px;border:1px solid #e5e7eb;box-shadow:0 18px 48px rgba(15,23,42,.22);padding:16px} +.clicd-fw-modal-title{font-size:16px;font-weight:700;color:#111827;margin-bottom:8px} +.clicd-fw-modal-body{font-size:14px;color:#4b5563;line-height:1.6;margin-bottom:14px} +.clicd-fw-modal-actions{display:flex;justify-content:flex-end;gap:8px} + + +
+
+ +
+
+
实例名称
+
{$container_name}
+
+
+
IP 地址
+
{$server_ip}
+
+
+ +
+
+ + 启用防火墙 + 加载中... +
+
+ +
添加规则
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ +
防火墙规则
+
+
加载中...
+
+ +

+
+    
+
+
确认删除
+
确认删除该规则?
+
+ + +
+
+
+
+ + diff --git a/Mofang/templates/info.html b/Mofang/templates/info.html index d6d27b9..d3440ab 100644 --- a/Mofang/templates/info.html +++ b/Mofang/templates/info.html @@ -1,4 +1,4 @@ -