mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-05-13 17:39:41 +00:00
122 lines
3.1 KiB
Go
122 lines
3.1 KiB
Go
package controller
|
|
|
|
import (
|
|
"archive/tar"
|
|
"archive/zip"
|
|
"compress/gzip"
|
|
"embed"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
//go:embed scripts/*
|
|
var scriptFS embed.FS
|
|
|
|
func serveScript(c *gin.Context) {
|
|
name := c.Param("name")
|
|
content, err := scriptFS.ReadFile(filepath.Join("scripts", name))
|
|
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Script not found"})
|
|
return
|
|
}
|
|
|
|
c.Data(http.StatusOK, "text/plain; charset=utf-8", content)
|
|
}
|
|
|
|
func serveAgentBinary(c *gin.Context) {
|
|
osType := c.Param("os")
|
|
arch := c.Param("arch")
|
|
|
|
// Nezha agent releases are usually .zip
|
|
// We will proxy the download from GitHub/Gitee and convert it to .tar.gz on the fly
|
|
// This allows using 'tar' in the install script and avoids GitHub connectivity issues
|
|
|
|
repo := "nezhahq/agent"
|
|
// For simplicity, we use the latest release
|
|
// In a real scenario, you might want to cache this or use a specific version
|
|
zipUrl := fmt.Sprintf("https://github.com/%s/releases/latest/download/nezha-agent_%s_%s.zip", repo, osType, arch)
|
|
|
|
resp, err := http.Get(zipUrl)
|
|
if err != nil || resp.StatusCode != http.StatusOK {
|
|
// Try Gitee if GitHub fails
|
|
zipUrl = fmt.Sprintf("https://gitee.com/naibahq/agent/releases/latest/download/nezha-agent_%s_%s.zip", osType, arch)
|
|
resp, err = http.Get(zipUrl)
|
|
if err != nil || resp.StatusCode != http.StatusOK {
|
|
c.JSON(http.StatusBadGateway, gin.H{"error": "Failed to fetch agent binary from upstream"})
|
|
return
|
|
}
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Create a temporary file to store the zip
|
|
tmpZip, err := os.CreateTemp("", "nezha-agent-*.zip")
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp file"})
|
|
return
|
|
}
|
|
defer os.Remove(tmpZip.Name())
|
|
defer tmpZip.Close()
|
|
|
|
// Limit the download to 200MB to prevent DoS
|
|
limitReader := io.LimitReader(resp.Body, 200*1024*1024)
|
|
if _, err := io.Copy(tmpZip, limitReader); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save zip"})
|
|
return
|
|
}
|
|
|
|
// Re-open for reading
|
|
zipReader, err := zip.OpenReader(tmpZip.Name())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open zip"})
|
|
return
|
|
}
|
|
defer zipReader.Close()
|
|
|
|
// Set headers for .tar.gz
|
|
c.Header("Content-Type", "application/gzip")
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=nezha-agent_%s_%s.tar.gz", osType, arch))
|
|
|
|
gw := gzip.NewWriter(c.Writer)
|
|
defer gw.Close()
|
|
tw := tar.NewWriter(gw)
|
|
defer tw.Close()
|
|
|
|
for _, f := range zipReader.File {
|
|
if f.FileInfo().IsDir() {
|
|
continue
|
|
}
|
|
|
|
// Security check: Limit uncompressed size to 100MB per file
|
|
if f.UncompressedSize64 > 100*1024*1024 {
|
|
continue
|
|
}
|
|
|
|
header, err := tar.FileInfoHeader(f.FileInfo(), "")
|
|
if err != nil {
|
|
continue
|
|
}
|
|
header.Name = f.Name
|
|
|
|
if err := tw.WriteHeader(header); err != nil {
|
|
continue
|
|
}
|
|
|
|
rc, err := f.Open()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
// Use LimitReader as a secondary defense for gosec (G110)
|
|
if _, err := io.Copy(tw, io.LimitReader(rc, 100*1024*1024)); err != nil {
|
|
rc.Close()
|
|
continue
|
|
}
|
|
rc.Close()
|
|
}
|
|
}
|