init 全新仓库,清除全部历史

This commit is contained in:
2026-07-09 09:54:43 +08:00
commit 0c2fe393df
154 changed files with 56586 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
# Node Exporter批量安装主机列表
# 格式:每行一个主机,支持以下格式:
# 1. 仅IP地址(默认端口22
# 2. IP:端口(指定SSH端口)
# 3. #开头的行是注释,会被忽略
# 4. 空行会被忽略
# 示例:
# 192.168.1.10
# 192.168.1.11:2222
# 192.168.1.12
# 请在下方添加您的主机:
10.150.10.83
10.150.10.86
10.150.10.87
+177
View File
@@ -0,0 +1,177 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Node Exporter 可视化管理面板</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container mt-4">
<h2 class="text-center mb-4">Node Exporter 批量管理面板</h2>
<!-- 单台主机操作区域 -->
<div class="card mb-4">
<div class="card-header">单台主机安装/卸载</div>
<div class="card-body row g-3">
<div class="col-auto">
<label>主机IP</label>
<input type="text" id="host" class="form-control" placeholder="10.150.10.83">
</div>
<div class="col-auto">
<label>SSH端口</label>
<input type="text" id="ssh_port" class="form-control" value="22">
</div>
<div class="col-auto">
<label>用户名</label>
<input type="text" id="user" class="form-control" value="root">
</div>
<div class="col-auto">
<label>密码</label>
<input type="password" id="pwd" class="form-control" placeholder="留空使用密钥">
</div>
<div class="col-auto align-self-end">
<button class="btn btn-success" onclick="singleInstall()">单台安装</button>
<button class="btn btn-danger" onclick="singleUninstall()">单台卸载</button>
<button class="btn btn-info" onclick="sshDiag()">SSH连通诊断</button>
</div>
</div>
</div>
<!-- 批量操作区域 -->
<div class="card mb-4">
<div class="card-header">批量操作(读取node_exporter_hosts.txt</div>
<div class="card-body">
<button class="btn btn-primary me-2" onclick="batchInstall()">批量安装</button>
<button class="btn btn-warning me-2" onclick="batchUninstall()">批量卸载</button>
<button class="btn btn-secondary me-2" onclick="getHosts()">刷新主机列表</button>
<button class="btn btn-dark" onclick="restartProm()">重启Prometheus</button>
<hr>
<h5>主机清单预览</h5>
<pre id="hosts_text" class="bg-light p-2"></pre>
</div>
</div>
<!-- 执行日志输出 -->
<div class="card">
<div class="card-header">执行实时日志</div>
<div class="card-body">
<textarea id="log_box" class="form-control bg-black text-white" rows="15" readonly></textarea>
</div>
</div>
<script>
function log(msg) {
const box = document.getElementById('log_box');
const time = new Date().toLocaleString();
if (msg === null || msg === undefined || msg === "") {
msg = "命令无输出或执行返回空";
}
box.value += `[${time}] ${msg}\n`;
box.scrollTop = box.scrollHeight;
}
// 统一处理接口返回
async function fetchApi(url) {
try {
const res = await fetch(url);
if (!res.ok) {
log("接口请求失败,HTTP状态码:" + res.status);
return null;
}
const data = await res.json();
return data;
} catch (e) {
log("网络/解析异常:" + e.message);
return null;
}
}
// 单台安装
async function singleInstall() {
const host = document.getElementById('host').value.trim();
const sp = document.getElementById('ssh_port').value.trim();
const user = document.getElementById('user').value.trim();
const pwd = document.getElementById('pwd').value;
if (!host) {
log("错误:请填写主机IP");
return;
}
log("开始执行单台安装:" + host);
const data = await fetchApi(`/api/single/install?host=${host}&ssh_port=${sp}&user=${user}&pwd=${pwd}`);
if (!data) return;
log(data.Msg);
if(data.Code === 1) log("执行错误:" + data.Err);
}
// 单台卸载
async function singleUninstall() {
const host = document.getElementById('host').value.trim();
const sp = document.getElementById('ssh_port').value.trim();
const user = document.getElementById('user').value.trim();
const pwd = document.getElementById('pwd').value;
if (!host) {
log("错误:请填写主机IP");
return;
}
log("开始执行单台卸载:" + host);
const data = await fetchApi(`/api/single/uninstall?host=${host}&ssh_port=${sp}&user=${user}&pwd=${pwd}`);
if (!data) return;
log(data.Msg);
if(data.Code === 1) log("执行错误:" + data.Err);
}
// 批量安装
async function batchInstall() {
log("开始批量安装所有清单主机");
const data = await fetchApi("/api/batch/install");
if (!data) return;
log(data.Msg);
if(data.Code === 1) log("执行错误:" + data.Err);
}
// 批量卸载
async function batchUninstall() {
log("开始批量卸载所有清单主机");
const data = await fetchApi("/api/batch/uninstall");
if (!data) return;
log(data.Msg);
if(data.Code === 1) log("执行错误:" + data.Err);
}
// 刷新主机列表
async function getHosts() {
const data = await fetchApi("/api/hosts/list");
if (!data) return;
document.getElementById('hosts_text').innerText = data.Msg;
}
// 重启Prometheus
async function restartProm() {
log("执行Prometheus重启");
const data = await fetchApi("/api/prom/restart");
if (!data) return;
log(data.Msg);
if(data.Code === 1) log("执行错误:" + data.Err);
}
// SSH诊断
async function sshDiag() {
const host = document.getElementById('host').value.trim();
const sp = document.getElementById('ssh_port').value.trim();
const user = document.getElementById('user').value.trim();
const pwd = document.getElementById('pwd').value;
if (!host) {
log("错误:请填写主机IP");
return;
}
log("开始SSH连通诊断:" + host);
const data = await fetchApi(`/api/ssh/diag?host=${host}&ssh_port=${sp}&user=${user}&pwd=${pwd}`);
if (!data) return;
log(data.Msg);
if(data.Code === 1) log("执行错误:" + data.Err);
}
// 页面加载自动刷新主机列表
window.onload = getHosts;
</script>
</body>
</html>
+188
View File
@@ -0,0 +1,188 @@
package main
import (
"encoding/json"
"fmt"
"net"
"net/http"
"os/exec"
"strings"
)
// 执行shell命令,返回完整输出
func runShell(cmdStr string) (string, error) {
args := strings.Fields(cmdStr)
cmd := exec.Command(args[0], args[1:]...)
out, err := cmd.CombinedOutput()
return string(out), err
}
// 统一返回JSON结构
type Resp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Err string `json:"err,omitempty"`
}
// 单节点安装接口
func singleInstallHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
r.ParseForm()
host := r.Form.Get("host")
sshPort := r.Form.Get("ssh_port")
user := r.Form.Get("user")
pwd := r.Form.Get("pwd")
cmd := fmt.Sprintf("./install_node_exporter.sh --cli --action single-install --host %s --ssh-port %s --user %s --pwd %s", host, sshPort, user, pwd)
output, err := runShell(cmd)
resp := Resp{Code: 0, Msg: output}
if err != nil {
resp.Code = 1
resp.Err = err.Error()
}
json.NewEncoder(w).Encode(resp)
}
// 单节点卸载接口
func singleUninstallHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
r.ParseForm()
host := r.Form.Get("host")
sshPort := r.Form.Get("ssh_port")
user := r.Form.Get("user")
pwd := r.Form.Get("pwd")
cmd := fmt.Sprintf("./install_node_exporter.sh --cli --action single-uninstall --host %s --ssh-port %s --user %s --pwd %s", host, sshPort, user, pwd)
output, err := runShell(cmd)
resp := Resp{Code: 0, Msg: output}
if err != nil {
resp.Code = 1
resp.Err = err.Error()
}
json.NewEncoder(w).Encode(resp)
}
// 批量安装接口
func batchInstallHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
cmd := "./install_node_exporter.sh --cli --action batch-install"
output, err := runShell(cmd)
resp := Resp{Code: 0, Msg: output}
if err != nil {
resp.Code = 1
resp.Err = err.Error()
}
json.NewEncoder(w).Encode(resp)
}
// 批量卸载接口
func batchUninstallHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
cmd := "./install_node_exporter.sh --cli --action batch-uninstall"
output, err := runShell(cmd)
resp := Resp{Code: 0, Msg: output}
if err != nil {
resp.Code = 1
resp.Err = err.Error()
}
json.NewEncoder(w).Encode(resp)
}
// 获取hosts主机列表
func getHostsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
cmd := "./install_node_exporter.sh --cli --action get-hosts"
output, err := runShell(cmd)
resp := Resp{Code: 0, Msg: output}
if err != nil {
resp.Code = 1
resp.Err = err.Error()
}
json.NewEncoder(w).Encode(resp)
}
// 重启Prometheus
func restartPromHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
cmd := "./install_node_exporter.sh --cli --action restart-prom"
output, err := runShell(cmd)
resp := Resp{Code: 0, Msg: output}
if err != nil {
resp.Code = 1
resp.Err = err.Error()
}
json.NewEncoder(w).Encode(resp)
}
// SSH诊断接口
func diagSshHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
r.ParseForm()
host := r.Form.Get("host")
sshPort := r.Form.Get("ssh_port")
user := r.Form.Get("user")
pwd := r.Form.Get("pwd")
cmd := fmt.Sprintf("./install_node_exporter.sh --cli --action diag-ssh --host %s --ssh-port %s --user %s --pwd %s", host, sshPort, user, pwd)
output, err := runShell(cmd)
resp := Resp{Code: 0, Msg: output}
if err != nil {
resp.Code = 1
resp.Err = err.Error()
}
json.NewEncoder(w).Encode(resp)
}
func main() {
// API路由
http.HandleFunc("/api/single/install", singleInstallHandler)
http.HandleFunc("/api/single/uninstall", singleUninstallHandler)
http.HandleFunc("/api/batch/install", batchInstallHandler)
http.HandleFunc("/api/batch/uninstall", batchUninstallHandler)
http.HandleFunc("/api/hosts/list", getHostsHandler)
http.HandleFunc("/api/prom/restart", restartPromHandler)
http.HandleFunc("/api/ssh/diag", diagSshHandler)
// 静态前端页面
http.Handle("/", http.FileServer(http.Dir("./static")))
localIP := getLocalIP()
fmt.Printf("Web管理面板启动成功,监听 0.0.0.0:808\n访问地址:http://%s:808\n", localIP)
err := http.ListenAndServe("0.0.0.0:808", nil)
if err != nil {
fmt.Println("服务启动失败:", err)
}
}
// 获取本机内网IP(简易工具)
func getLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "服务器IP"
}
for _, addr := range addrs {
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To4() != nil {
return ipNet.IP.String()
}
}
}
return "服务器IP"
}