feat: 工具配置增强、站点拨测与日志中心多项改进
- 工具配置:新增卸载 API/UI、Go 工具链、流水线构建自动安装依赖 - 修复 ListTools VerifyTool 递归栈溢出导致获取工具配置失败 - Node.js Alpine 安装改用 unofficial-builds musl 预编译包与 index.json 解析 LTS - 站点拨测:详情页路由、告警自动恢复、并发拨测与默认可观测频率 - 日志中心:RBAC、查询构建器、ES/Loki 增强与审计 request/response 展示 - 发布配置:构建节点、流水线远程执行、Deploy RBAC 导航拆分
This commit is contained in:
1 parent
cde7bbe540
commit
7e1c8f583c
84 files changed
+7729
-1507
No files matched your search
@@ -0,0 +1,61 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// BuildNode CI/CD 构建节点(Jenkins Agent 风格,基于 SSH 关联服务器资产) // Logan.Li
|
||||
type BuildNode struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
Name string `gorm:"size:64;not null;uniqueIndex" json:"name"`
|
||||
Description string `gorm:"size:255" json:"description"`
|
||||
ServerID uint `gorm:"not null;index" json:"server_id"` // FK → servers
|
||||
Labels string `gorm:"size:255;comment:标签(逗号分隔,用于调度匹配)" json:"labels,omitempty"`
|
||||
MaxExecutors int `gorm:"default:2;comment:最大并发构建数" json:"max_executors"`
|
||||
WorkspacePath string `gorm:"size:255;default:/data/aiops-workspace;comment:远程工作区根目录" json:"workspace_path"`
|
||||
Enabled bool `gorm:"default:true" json:"enabled"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
// 运行时统计(非持久化,由调度器填充)
|
||||
RunningCount int `gorm:"-" json:"running_count,omitempty"`
|
||||
Hostname string `gorm:"-" json:"hostname,omitempty"`
|
||||
IP string `gorm:"-" json:"ip,omitempty"`
|
||||
}
|
||||
|
||||
func (BuildNode) TableName() string {
|
||||
return "deploy_build_nodes"
|
||||
}
|
||||
|
||||
// BuildMasterConfig Master 内置构建节点并发配置(单行) // Logan.Li
|
||||
type BuildMasterConfig struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
MaxExecutors int `gorm:"default:2;not null;comment:最大并发(1=串行排队)" json:"max_executors"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (BuildMasterConfig) TableName() string { return "deploy_build_master_config" }
|
||||
|
||||
// UpdateBuildMasterRequest 更新 Master 并发数 // Logan.Li
|
||||
type UpdateBuildMasterRequest struct {
|
||||
MaxExecutors int `json:"max_executors" binding:"required,min=1,max=32"`
|
||||
}
|
||||
|
||||
// CreateBuildNodeRequest 创建构建节点
|
||||
type CreateBuildNodeRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
ServerID uint `json:"server_id" binding:"required"`
|
||||
Labels string `json:"labels"`
|
||||
MaxExecutors int `json:"max_executors"`
|
||||
WorkspacePath string `json:"workspace_path"`
|
||||
}
|
||||
|
||||
// UpdateBuildNodeRequest 更新构建节点
|
||||
type UpdateBuildNodeRequest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
ServerID *uint `json:"server_id"`
|
||||
Labels string `json:"labels"`
|
||||
MaxExecutors int `json:"max_executors"`
|
||||
WorkspacePath string `json:"workspace_path"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
}
|
||||
+32
-9
@@ -27,12 +27,19 @@ type Pipeline struct {
|
||||
// 并发控制
|
||||
MaxExecutors int `gorm:"default:2;comment:最大并发执行数(1=串行,默认2)" json:"max_executors"`
|
||||
|
||||
// 构建节点(nil=自动调度 Master 优先,非 nil=指定节点 ID) // Logan.Li
|
||||
BuildNodeID *uint `json:"build_node_id,omitempty"`
|
||||
|
||||
// SSH 部署目标服务器 (JSON: [1,2,3])
|
||||
ServerIDs string `gorm:"type:text;comment:SSH服务器ID列表(JSON)" json:"server_ids,omitempty"`
|
||||
ServerIDs string `gorm:"type:text;comment:SSH服务器ID列表(JSON)" json:"server_ids,omitempty"`
|
||||
DeployRemotePath string `gorm:"size:255;comment:SSH部署远程目录" json:"deploy_remote_path,omitempty"`
|
||||
DeployScript string `gorm:"type:text;comment:SSH部署后执行命令" json:"deploy_script,omitempty"`
|
||||
|
||||
// K8s 部署目标
|
||||
K8sClusterID *uint `json:"k8s_cluster_id,omitempty"` // FK → kubernetes_clusters
|
||||
K8sNamespace string `gorm:"size:128;comment:K8s部署命名空间" json:"k8s_namespace,omitempty"`
|
||||
K8sClusterID *uint `json:"k8s_cluster_id,omitempty"` // FK → kubernetes_clusters
|
||||
K8sNamespace string `gorm:"size:128;comment:K8s部署命名空间" json:"k8s_namespace,omitempty"`
|
||||
K8sDeployment string `gorm:"size:128;comment:K8s Deployment 名称" json:"k8s_deployment,omitempty"`
|
||||
K8sContainerName string `gorm:"size:128;comment:K8s更新镜像目标容器" json:"k8s_container_name,omitempty"`
|
||||
|
||||
// 仓库和镜像仓库关联(旧字段,待废弃)
|
||||
RepoID *uint `json:"repo_id"` // FK → deploy_pipeline_repos
|
||||
@@ -87,6 +94,8 @@ type PipelineRun struct {
|
||||
ImageTag string `gorm:"size:255" json:"image_tag"` // 构建产物镜像标签
|
||||
ArtifactURL string `gorm:"size:500" json:"artifact_url"` // 构建产物地址
|
||||
WorkspacePath string `gorm:"size:500" json:"workspace_path"` // 工作区路径
|
||||
BuildNodeID *uint `json:"build_node_id,omitempty"` // 实际执行节点(0/nil=Master)
|
||||
BuildNodeName string `gorm:"size:64" json:"build_node_name,omitempty"`
|
||||
Duration int `json:"duration"` // 秒
|
||||
Stages string `gorm:"type:text" json:"stages"` // JSON
|
||||
Logs string `gorm:"type:longtext" json:"logs"`
|
||||
@@ -153,9 +162,13 @@ type CreatePipelineRequest struct {
|
||||
// 部署模式
|
||||
DeployMode string `json:"deploy_mode"` // branch / tag
|
||||
DeployTarget string `json:"deploy_target"` // ssh / k8s
|
||||
ServerIDs string `json:"server_ids"` // SSH 服务器ID JSON [1,2,3]
|
||||
K8sClusterID *uint `json:"k8s_cluster_id"`
|
||||
K8sNamespace string `json:"k8s_namespace"`
|
||||
ServerIDs string `json:"server_ids"` // SSH 服务器ID JSON [1,2,3]
|
||||
DeployRemotePath string `json:"deploy_remote_path"`
|
||||
DeployScript string `json:"deploy_script"`
|
||||
K8sClusterID *uint `json:"k8s_cluster_id"`
|
||||
K8sNamespace string `json:"k8s_namespace"`
|
||||
K8sDeployment string `json:"k8s_deployment"`
|
||||
K8sContainerName string `json:"k8s_container_name"`
|
||||
|
||||
RepoID *uint `json:"repo_id"`
|
||||
RegistryID *uint `json:"registry_id"`
|
||||
@@ -176,6 +189,9 @@ type CreatePipelineRequest struct {
|
||||
|
||||
// 并发控制
|
||||
MaxExecutors int `json:"max_executors"`
|
||||
|
||||
// 构建节点
|
||||
BuildNodeID *uint `json:"build_node_id"`
|
||||
}
|
||||
|
||||
// UpdatePipelineRequest 更新流水线请求
|
||||
@@ -191,9 +207,13 @@ type UpdatePipelineRequest struct {
|
||||
Variables string `json:"variables"`
|
||||
DeployMode string `json:"deploy_mode"`
|
||||
DeployTarget string `json:"deploy_target"`
|
||||
ServerIDs string `json:"server_ids"`
|
||||
K8sClusterID *uint `json:"k8s_cluster_id"`
|
||||
K8sNamespace string `json:"k8s_namespace"`
|
||||
ServerIDs string `json:"server_ids"`
|
||||
DeployRemotePath string `json:"deploy_remote_path"`
|
||||
DeployScript string `json:"deploy_script"`
|
||||
K8sClusterID *uint `json:"k8s_cluster_id"`
|
||||
K8sNamespace string `json:"k8s_namespace"`
|
||||
K8sDeployment string `json:"k8s_deployment"`
|
||||
K8sContainerName string `json:"k8s_container_name"`
|
||||
RepoID *uint `json:"repo_id"`
|
||||
RegistryID *uint `json:"registry_id"`
|
||||
// 凭证关联
|
||||
@@ -212,6 +232,9 @@ type UpdatePipelineRequest struct {
|
||||
|
||||
// 并发控制
|
||||
MaxExecutors int `json:"max_executors"`
|
||||
|
||||
// 构建节点(传 null 清除绑定)
|
||||
BuildNodeID *uint `json:"build_node_id"`
|
||||
}
|
||||
|
||||
// TriggerPipelineRequest 触发流水线请求
|
||||
|
||||
+211
-50
@@ -74,26 +74,65 @@ func PredefinedTools() []DeployTool {
|
||||
InstallMethod: ToolLazy,
|
||||
BinaryName: "java",
|
||||
InstallPath: "/usr/local/bin/java",
|
||||
CheckCommand: "java -version 2>&1 | head -1",
|
||||
CheckCommand: "/usr/local/bin/java -version 2>&1 | head -1",
|
||||
VersionURL: "https://api.adoptium.net/v3/info/release_versions?heap_size=normal&image_type=jdk&page=0&page_size=1&project=jdk&release_type=ga&semver=false&sort_method=DEFAULT&sort_order=DESC&version=%5B17%2C99%5D",
|
||||
DownloadURL: "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-{version}/OpenJDK17U-jdk_x64_linux_hotspot_{version}.tar.gz",
|
||||
DefaultVersion: "17.0.13+7",
|
||||
InstallScript: `#!/bin/sh
|
||||
# JDK lazy install — Temurin OpenJDK
|
||||
# JDK lazy install — Alpine apk 优先,否则 Adoptium API 多镜像 // Logan.Li
|
||||
JDK_VERSION="${TOOL_VERSION:-17.0.13+7}"
|
||||
JDK_MAJOR=$(echo "$JDK_VERSION" | cut -d. -f1)
|
||||
JDK_URL="https://github.com/adoptium/temurin${JDK_MAJOR}-binaries/releases/download/jdk-${JDK_VERSION}/OpenJDK${JDK_MAJOR}U-jdk_x64_linux_hotspot_${JDK_VERSION}.tar.gz"
|
||||
echo "[JDK] Downloading ${JDK_VERSION}..."
|
||||
cd /tmp
|
||||
curl -sL "$JDK_URL" -o jdk.tar.gz
|
||||
JDK_FILE_VER=$(echo "$JDK_VERSION" | tr '+' '_')
|
||||
echo "[JDK] Installing ${JDK_VERSION} (major ${JDK_MAJOR})..."
|
||||
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
case "$JDK_MAJOR" in
|
||||
11) APK_PKG="openjdk11-jdk" ;;
|
||||
17) APK_PKG="openjdk17-jdk" ;;
|
||||
21) APK_PKG="openjdk21-jdk" ;;
|
||||
*) APK_PKG="" ;;
|
||||
esac
|
||||
if [ -n "$APK_PKG" ]; then
|
||||
echo "[JDK] Trying apk: $APK_PKG"
|
||||
if apk add --no-cache "$APK_PKG"; then
|
||||
JAVA_BIN=$(command -v java 2>/dev/null || echo "/usr/lib/jvm/java-${JDK_MAJOR}-openjdk/bin/java")
|
||||
mkdir -p /usr/local/bin /usr/local/share/jdk/bin
|
||||
ln -sf "$JAVA_BIN" /usr/local/bin/java
|
||||
ln -sf "$(dirname "$JAVA_BIN")/javac" /usr/local/bin/javac 2>/dev/null || true
|
||||
ln -sf "$(dirname "$JAVA_BIN")/jar" /usr/local/bin/jar 2>/dev/null || true
|
||||
echo "[JDK] Installed via apk: $(java -version 2>&1 | head -1)"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
cd /tmp && rm -rf jdk.tar.gz jdk-install
|
||||
mkdir -p jdk-install && cd jdk-install
|
||||
DOWNLOADED=0
|
||||
for URL in \
|
||||
"https://api.adoptium.net/v3/binary/version/${JDK_VERSION}/linux/x64/jdk/hotspot/normal/eclipse?project=jdk" \
|
||||
"https://mirrors.tuna.tsinghua.edu.cn/Adoptium/${JDK_MAJOR}/jdk/${JDK_VERSION}/OpenJDK${JDK_MAJOR}U-jdk_x64_linux_hotspot_${JDK_FILE_VER}.tar.gz" \
|
||||
"https://github.com/adoptium/temurin${JDK_MAJOR}-binaries/releases/download/jdk-${JDK_VERSION}/OpenJDK${JDK_MAJOR}U-jdk_x64_linux_hotspot_${JDK_FILE_VER}.tar.gz"
|
||||
do
|
||||
echo "[JDK] Trying $URL"
|
||||
if curl -fsSL -L -o jdk.tar.gz "$URL"; then
|
||||
DOWNLOADED=1
|
||||
break
|
||||
fi
|
||||
rm -f jdk.tar.gz
|
||||
done
|
||||
[ "$DOWNLOADED" -eq 1 ] || { echo "[JDK] all mirrors failed"; exit 1; }
|
||||
tar xzf jdk.tar.gz
|
||||
JDK_DIR=$(find . -maxdepth 1 -type d -name 'jdk-*' | head -1)
|
||||
[ -n "$JDK_DIR" ] || { echo "[JDK] extract dir not found"; exit 1; }
|
||||
mkdir -p /usr/local/share
|
||||
mv jdk-${JDK_VERSION} /usr/local/share/jdk
|
||||
rm -rf /usr/local/share/jdk
|
||||
mv "$JDK_DIR" /usr/local/share/jdk
|
||||
ln -sf /usr/local/share/jdk/bin/java /usr/local/bin/java
|
||||
ln -sf /usr/local/share/jdk/bin/javac /usr/local/bin/javac
|
||||
ln -sf /usr/local/share/jdk/bin/jar /usr/local/bin/jar
|
||||
rm -f jdk.tar.gz
|
||||
echo "[JDK] Installed: $(java -version 2>&1 | head -1)"
|
||||
cd /tmp && rm -rf jdk-install
|
||||
echo "[JDK] Installed: $(/usr/local/bin/java -version 2>&1 | head -1)"
|
||||
`,
|
||||
SortOrder: 1,
|
||||
},
|
||||
@@ -106,26 +145,100 @@ echo "[JDK] Installed: $(java -version 2>&1 | head -1)"
|
||||
InstallMethod: ToolLazy,
|
||||
BinaryName: "mvn",
|
||||
InstallPath: "/usr/local/bin/mvn",
|
||||
CheckCommand: "mvn --version 2>/dev/null",
|
||||
CheckCommand: "/usr/local/bin/mvn --version 2>/dev/null | head -1",
|
||||
DownloadURL: "https://dlcdn.apache.org/maven/maven-3/{version}/binaries/apache-maven-{version}-bin.tar.gz",
|
||||
VersionURL: "https://dlcdn.apache.org/maven/maven-3/",
|
||||
DefaultVersion: "3.9.9",
|
||||
InstallScript: `#!/bin/sh
|
||||
# Maven lazy install — download & extract
|
||||
# Maven lazy install — 多镜像轮询 // Logan.Li
|
||||
MAVEN_VERSION="${TOOL_VERSION:-3.9.9}"
|
||||
MAVEN_URL="https://dlcdn.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz"
|
||||
echo "[Maven] Downloading ${MAVEN_VERSION}..."
|
||||
cd /tmp
|
||||
curl -sL "$MAVEN_URL" -o maven.tar.gz
|
||||
cd /tmp && rm -rf maven.tar.gz maven-install
|
||||
mkdir -p maven-install && cd maven-install
|
||||
DOWNLOADED=0
|
||||
for URL in \
|
||||
"https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \
|
||||
"https://repo.huaweicloud.com/apache/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \
|
||||
"https://dlcdn.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \
|
||||
"https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz"
|
||||
do
|
||||
echo "[Maven] Trying $URL"
|
||||
if curl -fsSL -o maven.tar.gz "$URL"; then
|
||||
DOWNLOADED=1
|
||||
break
|
||||
fi
|
||||
rm -f maven.tar.gz
|
||||
done
|
||||
[ "$DOWNLOADED" -eq 1 ] || { echo "[Maven] all mirrors failed"; exit 1; }
|
||||
tar xzf maven.tar.gz
|
||||
MVN_DIR=$(find . -maxdepth 1 -type d -name 'apache-maven-*' | head -1)
|
||||
[ -n "$MVN_DIR" ] || { echo "[Maven] extract dir not found"; exit 1; }
|
||||
mkdir -p /usr/local/share
|
||||
mv apache-maven-${MAVEN_VERSION} /usr/local/share/maven
|
||||
rm -rf /usr/local/share/maven
|
||||
mv "$MVN_DIR" /usr/local/share/maven
|
||||
ln -sf /usr/local/share/maven/bin/mvn /usr/local/bin/mvn
|
||||
rm -f maven.tar.gz
|
||||
echo "[Maven] Installed: $(mvn --version 2>&1 | head -1)"
|
||||
cd /tmp && rm -rf maven-install
|
||||
echo "[Maven] Installed: $(/usr/local/bin/mvn --version 2>&1 | head -1)"
|
||||
`,
|
||||
SortOrder: 2,
|
||||
},
|
||||
// ──── Go — 后端构建 // Logan.Li ────
|
||||
{
|
||||
Name: "golang",
|
||||
DisplayName: "Go (Golang)",
|
||||
Description: "Go 语言工具链,流水线 go build/test 阶段使用",
|
||||
Category: "build",
|
||||
InstallMethod: ToolLazy,
|
||||
BinaryName: "go",
|
||||
InstallPath: "/usr/local/bin/go",
|
||||
CheckCommand: "/usr/local/bin/go version 2>/dev/null",
|
||||
DownloadURL: "https://go.dev/dl/go{version}.linux-amd64.tar.gz",
|
||||
DefaultVersion: "1.24.0",
|
||||
InstallScript: `#!/bin/sh
|
||||
# Go lazy install — 国内镜像优先 // Logan.Li
|
||||
GO_VERSION="${TOOL_VERSION:-1.24.0}"
|
||||
GO_VERSION="${GO_VERSION#go}"
|
||||
echo "[Go] Installing go${GO_VERSION}..."
|
||||
cd /tmp && rm -rf go.tar.gz go-install
|
||||
mkdir -p go-install && cd go-install
|
||||
DOWNLOADED=0
|
||||
for URL in \
|
||||
"https://golang.google.cn/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
"https://mirrors.aliyun.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
"https://mirrors.ustc.edu.cn/golang/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
"https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
do
|
||||
echo "[Go] Trying $URL"
|
||||
if curl -fsSL -L -o go.tar.gz "$URL"; then
|
||||
DOWNLOADED=1
|
||||
break
|
||||
fi
|
||||
rm -f go.tar.gz
|
||||
done
|
||||
[ "$DOWNLOADED" -eq 1 ] || { echo "[Go] all mirrors failed"; exit 1; }
|
||||
tar xzf go.tar.gz
|
||||
[ -d go ] || { echo "[Go] extract dir not found"; exit 1; }
|
||||
rm -rf /usr/local/go
|
||||
mv go /usr/local/go
|
||||
mkdir -p /usr/local/bin
|
||||
ln -sf /usr/local/go/bin/go /usr/local/bin/go
|
||||
ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
|
||||
mkdir -p /etc/profile.d
|
||||
cat > /etc/profile.d/golang.sh << 'GO_PROFILE'
|
||||
export GOROOT=/usr/local/go
|
||||
export PATH=$GOROOT/bin:$PATH
|
||||
export GOPROXY=https://goproxy.cn,direct
|
||||
export GOTOOLCHAIN=local
|
||||
GO_PROFILE
|
||||
chmod +x /etc/profile.d/golang.sh
|
||||
export GOROOT=/usr/local/go
|
||||
export PATH=$GOROOT/bin:$PATH
|
||||
export GOPROXY=https://goproxy.cn,direct
|
||||
export GOTOOLCHAIN=local
|
||||
echo "[Go] Installed: $(/usr/local/bin/go version)"
|
||||
`,
|
||||
SortOrder: 3,
|
||||
},
|
||||
// ──── NVM — Node Version Manager // Logan.Li ────
|
||||
{
|
||||
Name: "nvm",
|
||||
@@ -135,26 +248,30 @@ echo "[Maven] Installed: $(mvn --version 2>&1 | head -1)"
|
||||
InstallMethod: ToolLazy,
|
||||
BinaryName: "nvm",
|
||||
InstallPath: "/usr/local/share/nvm/nvm.sh",
|
||||
CheckCommand: "source /usr/local/share/nvm/nvm.sh 2>/dev/null && nvm --version 2>/dev/null",
|
||||
CheckCommand: ". /usr/local/share/nvm/nvm.sh 2>/dev/null && nvm --version 2>/dev/null",
|
||||
VersionURL: "https://api.github.com/repos/nvm-sh/nvm/releases/latest",
|
||||
DefaultVersion: "0.40.1",
|
||||
InstallScript: `#!/bin/sh
|
||||
# NVM lazy install
|
||||
# NVM lazy install — 使用 bash 执行官方脚本 // Logan.Li
|
||||
NVM_VERSION="${TOOL_VERSION:-0.40.1}"
|
||||
NVM_DIR="/usr/local/share/nvm"
|
||||
echo "[NVM] Downloading ${NVM_VERSION}..."
|
||||
echo "[NVM] Downloading v${NVM_VERSION}..."
|
||||
mkdir -p "$NVM_DIR"
|
||||
curl -sL "https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh" | NVM_DIR="$NVM_DIR" bash
|
||||
# Make available system-wide via profile.d
|
||||
curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh" -o /tmp/nvm-install.sh
|
||||
export NVM_DIR
|
||||
bash /tmp/nvm-install.sh
|
||||
rm -f /tmp/nvm-install.sh
|
||||
[ -s "$NVM_DIR/nvm.sh" ] || { echo "[NVM] nvm.sh not found"; exit 1; }
|
||||
mkdir -p /etc/profile.d
|
||||
cat > /etc/profile.d/nvm.sh << 'NVM_PROFILE'
|
||||
export NVM_DIR="/usr/local/share/nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||||
NVM_PROFILE
|
||||
chmod +x /etc/profile.d/nvm.sh
|
||||
echo "[NVM] Installed: v${NVM_VERSION}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
echo "[NVM] Installed: $(nvm --version 2>/dev/null || echo v${NVM_VERSION})"
|
||||
`,
|
||||
SortOrder: 3,
|
||||
SortOrder: 4,
|
||||
},
|
||||
// ──── Node.js — 通过 NVM 安装 // Logan.Li ────
|
||||
{
|
||||
@@ -166,30 +283,74 @@ echo "[NVM] Installed: v${NVM_VERSION}"
|
||||
BinaryName: "node",
|
||||
InstallPath: "/usr/local/bin/node",
|
||||
CheckCommand: "node --version 2>/dev/null",
|
||||
DefaultVersion: "v22.14.0",
|
||||
DefaultVersion: "lts",
|
||||
InstallScript: `#!/bin/sh
|
||||
# Node.js via NVM install
|
||||
NODE_VERSION="${TOOL_VERSION:-v22.14.0}"
|
||||
echo "[Node.js] Installing via NVM ${NODE_VERSION}..."
|
||||
export NVM_DIR="/usr/local/share/nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
if ! command -v nvm >/dev/null 2>&1; then
|
||||
# NVM not found — install it first
|
||||
echo "[Node.js] NVM not found, installing NVM first..."
|
||||
curl -sL "https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh" | NVM_DIR="$NVM_DIR" bash
|
||||
\. "$NVM_DIR/nvm.sh"
|
||||
fi
|
||||
nvm install "${NODE_VERSION#v}"
|
||||
nvm alias default "${NODE_VERSION#v}"
|
||||
nvm use "${NODE_VERSION#v}"
|
||||
# Symlink for system-wide access
|
||||
ln -sf "$NVM_DIR/versions/node/$(nvm version)/bin/node" /usr/local/bin/node
|
||||
ln -sf "$NVM_DIR/versions/node/$(nvm version)/bin/npm" /usr/local/bin/npm
|
||||
ln -sf "$NVM_DIR/versions/node/$(nvm version)/bin/npx" /usr/local/bin/npx
|
||||
npm config set registry https://registry.npmmirror.com
|
||||
echo "[Node.js] Installed: $(node --version) / npm $(npm --version)"
|
||||
# Node.js 安装 — Alpine 用 unofficial-builds musl 包(官方已停 musl 预编译)// Logan.Li
|
||||
NODE_VERSION="${TOOL_VERSION:-lts}"
|
||||
NVM_DIR="/usr/local/share/nvm"
|
||||
NODE_MIRROR="${NODE_MIRROR:-https://npmmirror.com/mirrors/node}"
|
||||
echo "[Node.js] Installing (${NODE_VERSION})..."
|
||||
mkdir -p "$NVM_DIR/versions/node"
|
||||
export NVM_DIR
|
||||
|
||||
resolve_version() {
|
||||
case "$1" in
|
||||
lts)
|
||||
curl -fsSL "https://nodejs.org/dist/index.json" 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print(next(x['version'] for x in d if x.get('lts')))" 2>/dev/null || echo "v24.18.0"
|
||||
;;
|
||||
latest|node)
|
||||
curl -fsSL "https://nodejs.org/dist/index.json" 2>/dev/null | python3 -c "import json,sys; print(json.load(sys.stdin)[0]['version'])" 2>/dev/null || echo "v24.18.0"
|
||||
;;
|
||||
*)
|
||||
echo "v${1#v}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
direct_install_binary() {
|
||||
RESOLVED="$1"
|
||||
DEST="$NVM_DIR/versions/node/${RESOLVED}"
|
||||
TMP="/tmp/node-dl-$$"
|
||||
rm -rf "$TMP" "$DEST"
|
||||
mkdir -p "$TMP"
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
# Node 官方仅 glibc;Alpine musl 走 unofficial-builds // Logan.Li
|
||||
URLS="https://unofficial-builds.nodejs.org/download/release/${RESOLVED}/node-${RESOLVED}-linux-x64-musl.tar.xz"
|
||||
else
|
||||
URLS="${NODE_MIRROR}/${RESOLVED}/node-${RESOLVED}-linux-x64.tar.xz https://nodejs.org/dist/${RESOLVED}/node-${RESOLVED}-linux-x64.tar.xz"
|
||||
fi
|
||||
for URL in $URLS; do
|
||||
echo "[Node.js] Downloading $URL"
|
||||
rm -rf "$TMP"/* 2>/dev/null || true
|
||||
if curl -fsSL -L "$URL" | tar -xJ -C "$TMP" 2>/dev/null; then
|
||||
SUB=$(find "$TMP" -mindepth 1 -maxdepth 1 -type d | head -1)
|
||||
if [ -n "$SUB" ] && [ -x "$SUB/bin/node" ]; then
|
||||
mv "$SUB" "$DEST"
|
||||
rm -rf "$TMP"
|
||||
echo "[Node.js] Installed $RESOLVED"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
echo "[Node.js] Failed: $URL"
|
||||
done
|
||||
rm -rf "$TMP"
|
||||
return 1
|
||||
}
|
||||
|
||||
RESOLVED=$(resolve_version "$NODE_VERSION")
|
||||
[ -n "$RESOLVED" ] || { echo "[Node.js] cannot resolve version for $NODE_VERSION"; exit 1; }
|
||||
echo "[Node.js] Target version: $RESOLVED"
|
||||
direct_install_binary "$RESOLVED" || { echo "[Node.js] binary install failed"; exit 1; }
|
||||
|
||||
INST="$RESOLVED"
|
||||
mkdir -p /usr/local/bin
|
||||
ln -sf "$NVM_DIR/versions/node/${INST}/bin/node" /usr/local/bin/node
|
||||
ln -sf "$NVM_DIR/versions/node/${INST}/bin/npm" /usr/local/bin/npm
|
||||
ln -sf "$NVM_DIR/versions/node/${INST}/bin/npx" /usr/local/bin/npx
|
||||
"$NVM_DIR/versions/node/${INST}/bin/npm" config set registry https://registry.npmmirror.com
|
||||
echo "[Node.js] Installed: $(/usr/local/bin/node --version) / npm $("$NVM_DIR/versions/node/${INST}/bin/npm" --version)"
|
||||
`,
|
||||
SortOrder: 4,
|
||||
SortOrder: 5,
|
||||
},
|
||||
// ──── Git — 版本控制(懒加载, U4 镜像瘦身)// Logan.Li ────
|
||||
{
|
||||
@@ -202,7 +363,7 @@ echo "[Node.js] Installed: $(node --version) / npm $(npm --version)"
|
||||
InstallPath: "/usr/bin/git",
|
||||
CheckCommand: "git --version 2>/dev/null",
|
||||
InstallScript: `apk add --no-cache git`,
|
||||
SortOrder: 5,
|
||||
SortOrder: 6,
|
||||
},
|
||||
// ──── Docker CLI — 容器构建(懒加载, U4 镜像瘦身)// Logan.Li ────
|
||||
{
|
||||
@@ -215,7 +376,7 @@ echo "[Node.js] Installed: $(node --version) / npm $(npm --version)"
|
||||
InstallPath: "/usr/bin/docker",
|
||||
CheckCommand: "docker --version 2>/dev/null",
|
||||
InstallScript: `apk add --no-cache docker-cli`,
|
||||
SortOrder: 6,
|
||||
SortOrder: 7,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// LogScopeRole 日志查询范围权限(正则匹配 ES 索引 / Loki label) // Logan.Li
|
||||
type LogScopeRole struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
Name string `gorm:"size:64;not null" json:"name"`
|
||||
Code string `gorm:"size:64;uniqueIndex;not null" json:"code"`
|
||||
Engine string `gorm:"size:32;default:*;comment:引擎 loki/elasticsearch/*" json:"engine"`
|
||||
MatchField string `gorm:"size:32;not null;comment:匹配维度 index/app/namespace/job" json:"match_field"`
|
||||
Pattern string `gorm:"size:255;not null;comment:正则表达式" json:"pattern"`
|
||||
DatasourceID *uint `json:"datasource_id,omitempty"`
|
||||
Role string `gorm:"size:16;default:viewer;comment:viewer/analyst" json:"role"`
|
||||
Description string `gorm:"size:255" json:"description"`
|
||||
Status int `gorm:"default:1;comment:0禁用1启用" json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (LogScopeRole) TableName() string { return "monitor_log_scope_roles" }
|
||||
|
||||
// UserLogScopeRole 用户 ↔ 日志范围权限 // Logan.Li
|
||||
type UserLogScopeRole struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
UserID uint `gorm:"uniqueIndex:idx_user_log_scope;not null" json:"user_id"`
|
||||
LogScopeRoleID uint `gorm:"uniqueIndex:idx_user_log_scope;not null" json:"log_scope_role_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
LogScopeRole *LogScopeRole `json:"log_scope_role,omitempty" gorm:"foreignKey:LogScopeRoleID"`
|
||||
}
|
||||
|
||||
func (UserLogScopeRole) TableName() string { return "monitor_user_log_scope_roles" }
|
||||
|
||||
// CreateLogScopeRoleRequest 创建日志权限策略 // Logan.Li
|
||||
type CreateLogScopeRoleRequest struct {
|
||||
Name string `json:"name" binding:"required,max=64"`
|
||||
Code string `json:"code" binding:"required,max=64"`
|
||||
Engine string `json:"engine" binding:"omitempty,oneof=loki elasticsearch *"`
|
||||
MatchField string `json:"match_field" binding:"required,oneof=index app namespace job"`
|
||||
Pattern string `json:"pattern" binding:"required,max=255"`
|
||||
DatasourceID *uint `json:"datasource_id"`
|
||||
Role string `json:"role" binding:"omitempty,oneof=viewer analyst"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// UpdateLogScopeRoleRequest 更新日志权限策略 // Logan.Li
|
||||
type UpdateLogScopeRoleRequest struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Engine string `json:"engine"`
|
||||
MatchField string `json:"match_field"`
|
||||
Pattern string `json:"pattern"`
|
||||
DatasourceID *uint `json:"datasource_id"`
|
||||
Role string `json:"role"`
|
||||
Description string `json:"description"`
|
||||
Status *int `json:"status"`
|
||||
}
|
||||
|
||||
// AssignLogScopeRoleRequest 用户授权(user_id 取自 URL 路径)// Logan.Li
|
||||
type AssignLogScopeRoleRequest struct {
|
||||
LogScopeRoleID uint `json:"log_scope_role_id" binding:"required"`
|
||||
}
|
||||
|
||||
// PreviewLogScopeRequest 命中预览 // Logan.Li
|
||||
type PreviewLogScopeRequest struct {
|
||||
Engine string `json:"engine" binding:"required,oneof=loki elasticsearch"`
|
||||
DatasourceID uint `json:"datasource_id"`
|
||||
Index string `json:"index"`
|
||||
Query string `json:"query"`
|
||||
SampleValue string `json:"sample_value"`
|
||||
}
|
||||
Reference in new issue
Block a user