| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- package main
- import (
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "time"
- "hnyfkj.com.cn/rtu/linux/baseapp"
- )
- const maxUploadSize = 1 << 30 // 1GB
- func downloadAppInstallLog(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- filename := fmt.Sprintf("app-install.log.%s.tar.gz", time.Now().Format("20060102150405"))
- tmpFile := fmt.Sprintf("/tmp/%d.tar.gz", time.Now().UnixNano())
- defer os.Remove(tmpFile)
- cmd := exec.Command(
- "tar",
- "--warning=no-file-changed",
- "-czf",
- tmpFile,
- "-C",
- "/opt/yfkj/app-install.service/log",
- ".",
- )
- out, err := cmd.CombinedOutput()
- if err != nil {
- if exitErr, ok := err.(*exec.ExitError); !ok || exitErr.ExitCode() != 1 {
- http.Error(w, string(out), http.StatusInternalServerError)
- return
- }
- }
- w.Header().Set("Content-Type", "application/gzip")
- w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
- http.ServeFile(w, r, tmpFile)
- }
- func restartAppInstallService(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- cmd := exec.Command("systemctl", "restart", "yfkj-app-install.service")
- if out, err := cmd.CombinedOutput(); err != nil {
- http.Error(w, string(out)+err.Error(), http.StatusInternalServerError)
- return
- }
- w.Header().Set("Content-Type", "application/json")
- w.Write([]byte(`{"status":"ok"}`))
- }
- func getUserAppStatus() (string, string) {
- status := noValue
- pid := noValue
- out, err := exec.Command("systemctl", "show", "yfkj-user-app.service",
- "-p", "LoadState",
- "-p", "ActiveState",
- "-p", "SubState",
- "-p", "MainPID").Output()
- if err != nil {
- return status, pid
- }
- v := string(out)
- switch {
- case strings.Contains(v, "ActiveState=active") &&
- strings.Contains(v, "SubState=running"):
- status = "🟢运行中"
- case strings.Contains(v, "ActiveState=active") &&
- strings.Contains(v, "SubState=exited"):
- status = "🔵已完成"
- case strings.Contains(v, "LoadState=loaded") &&
- strings.Contains(v, "ActiveState=inactive"):
- status = "🟡已停止"
- }
- for _, line := range strings.Split(v, "\n") {
- if after, ok := strings.CutPrefix(line, "MainPID="); ok {
- if after != "" && after != "0" {
- pid = after
- }
- break
- }
- }
- return status, pid
- }
- func appInstallStatusHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- var info *struct {
- Pkg struct {
- FileName string `json:"fileName"`
- FileSize int64 `json:"fileSize"`
- FileMD5 string `json:"fileMD5"`
- UploadTime string `json:"uploadTime"`
- } `json:"pkg"`
- App struct {
- Name string `json:"name"`
- Version string `json:"version"`
- Executable string `json:"executable"`
- Description string `json:"description"`
- ReadmeFile string `json:"readmeFile"`
- } `json:"app"`
- Ins struct {
- Installed bool `json:"installed"`
- InstallTime string `json:"installTime"`
- } `json:"ins"`
- }
- callErr := callRPCResult(r.Context(), 7004, "core.getInstallInfo", nil, &info)
- data := map[string]string{
- "state": "0",
- "avar1": noValue,
- "avar2": noValue,
- "avar3": noValue,
- "avar4": noValue,
- "bvar1": noValue,
- "bvar2": noValue,
- "bvar3": noValue,
- "bvar4": noValue,
- "bvar5": noValue,
- "cvar1": noValue,
- "cvar2": noValue,
- "cvar3": noValue,
- "cvar4": noValue,
- "cvar5": noValue,
- }
- if callErr == nil {
- data["avar1"] = "🟢正常"
- if info != nil {
- data["bvar1"] = info.Pkg.FileName
- data["bvar2"] = fmt.Sprintf("%d 字节", info.Pkg.FileSize)
- data["bvar3"] = info.Pkg.FileMD5
- data["bvar4"] = info.Pkg.UploadTime
- data["cvar1"] = info.App.Name
- data["cvar2"] = info.App.Version
- data["cvar3"] = info.App.Executable
- data["cvar4"] = info.Ins.InstallTime
- data["cvar5"] = info.App.ReadmeFile
- if info.Ins.Installed {
- data["state"] = "2"
- data["avar2"] = info.App.Name
- data["avar3"], data["avar4"] = getUserAppStatus()
- data["bvar5"] = "已安装"
- } else {
- data["state"] = "1"
- data["bvar5"] = "未安装"
- }
- }
- }
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- json.NewEncoder(w).Encode(data)
- }
- func uploadUserAppPkg(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
- mr, err := r.MultipartReader()
- if err != nil {
- http.Error(w, "解析上传请求失败: "+err.Error(), http.StatusBadRequest)
- return
- }
- for {
- part, err := mr.NextPart()
- if err == io.EOF {
- break
- }
- if err != nil {
- http.Error(w, "读取上传数据失败: "+err.Error(), http.StatusBadRequest)
- return
- }
- if part.FormName() != "file" {
- part.Close()
- continue
- }
- filename := filepath.Base(part.FileName())
- if filename == "" || filename == "." {
- part.Close()
- http.Error(w, "文件名为空", http.StatusBadRequest)
- return
- }
- if !strings.HasSuffix(strings.ToLower(filename), ".tar.gz") {
- part.Close()
- http.Error(w, "只允许上传 tar.gz 类型的文件", http.StatusBadRequest)
- return
- }
- path := filepath.Join("/tmp", filename)
- err = saveUploadFile(part, path)
- part.Close()
- if err != nil {
- http.Error(w, "保存上传文件失败: "+err.Error(), http.StatusInternalServerError)
- return
- }
- size := int64(0)
- if stat, err := os.Stat(path); err == nil {
- size = stat.Size()
- }
- baseapp.Logger.Infof(
- "[上传文件成功] 上传文件名: %s, 上传文件大小: %d 字节, 本地存储路径: %s",
- filename, size, path)
- break
- }
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- json.NewEncoder(w).Encode(map[string]string{"result": "success"})
- }
- func saveUploadFile(src io.Reader, path string) error {
- dst, err := os.Create(path)
- if err != nil {
- return err
- }
- defer dst.Close()
- _, err = io.Copy(dst, src)
- if err != nil {
- os.Remove(path)
- return err
- }
- return nil
- }
- func appPkgImportHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- var params struct {
- File string `json:"file"`
- }
- if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if params.File == "" {
- http.Error(w, "missing file", http.StatusBadRequest)
- return
- }
- callErr := callRPCResult(r.Context(), 7004, "core.package.import",
- map[string]string{"file": params.File}, nil)
- if callErr != nil {
- http.Error(w, callErr.Error(), http.StatusInternalServerError)
- return
- }
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- json.NewEncoder(w).Encode(map[string]string{"result": "success"})
- }
- func appPkgRemoveHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- callErr := callRPCResult(r.Context(), 7004, "core.package.remove", nil, nil)
- if callErr != nil {
- http.Error(w, callErr.Error(), http.StatusInternalServerError)
- return
- }
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- json.NewEncoder(w).Encode(map[string]string{"result": "success"})
- }
- func appInstallHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- callErr := callRPCResult(r.Context(), 7004, "core.app.install", nil, nil)
- if callErr != nil {
- http.Error(w, callErr.Error(), http.StatusInternalServerError)
- return
- }
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- json.NewEncoder(w).Encode(map[string]string{"result": "success"})
- }
- func appUninstallHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- callErr := callRPCResult(r.Context(), 7004, "core.app.uninstall", nil, nil)
- if callErr != nil {
- http.Error(w, callErr.Error(), http.StatusInternalServerError)
- return
- }
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- json.NewEncoder(w).Encode(map[string]string{"result": "success"})
- }
- func restartUserAppService(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- cmd := exec.Command("systemctl", "restart", "yfkj-user-app.service")
- if out, err := cmd.CombinedOutput(); err != nil {
- http.Error(w, string(out)+err.Error(), http.StatusInternalServerError)
- return
- }
- w.Header().Set("Content-Type", "application/json")
- w.Write([]byte(`{"status":"ok"}`))
- }
- var userAppPkgDir = "/opt/yfkj/app-install.service/user-app-pkg"
- func downloadUserAppPkg(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
- return
- }
- var info struct {
- Pkg struct {
- FileName string `json:"fileName"`
- } `json:"pkg"`
- }
- data, err := os.ReadFile(filepath.Join(userAppPkgDir, "appins.json"))
- if err != nil {
- http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
- return
- }
- if err := json.Unmarshal(data, &info); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- if info.Pkg.FileName == "" {
- http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
- return
- }
- filename := filepath.Base(info.Pkg.FileName)
- file := filepath.Join(userAppPkgDir, filename)
- stat, err := os.Stat(file)
- if err != nil || !stat.Mode().IsRegular() {
- http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
- return
- }
- w.Header().Set("Content-Type", "application/gzip")
- w.Header().Set("Content-Disposition",
- fmt.Sprintf(`attachment; filename="%s"`, filename))
- http.ServeFile(w, r, file)
- }
|