page6_handler.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "time"
  12. "hnyfkj.com.cn/rtu/linux/baseapp"
  13. )
  14. const maxUploadSize = 1 << 30 // 1GB
  15. func downloadAppInstallLog(w http.ResponseWriter, r *http.Request) {
  16. if r.Method != http.MethodGet {
  17. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  18. return
  19. }
  20. filename := fmt.Sprintf("app-install.log.%s.tar.gz", time.Now().Format("20060102150405"))
  21. tmpFile := fmt.Sprintf("/tmp/%d.tar.gz", time.Now().UnixNano())
  22. defer os.Remove(tmpFile)
  23. cmd := exec.Command(
  24. "tar",
  25. "--warning=no-file-changed",
  26. "-czf",
  27. tmpFile,
  28. "-C",
  29. "/opt/yfkj/app-install.service/log",
  30. ".",
  31. )
  32. out, err := cmd.CombinedOutput()
  33. if err != nil {
  34. if exitErr, ok := err.(*exec.ExitError); !ok || exitErr.ExitCode() != 1 {
  35. http.Error(w, string(out), http.StatusInternalServerError)
  36. return
  37. }
  38. }
  39. w.Header().Set("Content-Type", "application/gzip")
  40. w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
  41. http.ServeFile(w, r, tmpFile)
  42. }
  43. func restartAppInstallService(w http.ResponseWriter, r *http.Request) {
  44. if r.Method != http.MethodPost {
  45. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  46. return
  47. }
  48. cmd := exec.Command("systemctl", "restart", "yfkj-app-install.service")
  49. if out, err := cmd.CombinedOutput(); err != nil {
  50. http.Error(w, string(out)+err.Error(), http.StatusInternalServerError)
  51. return
  52. }
  53. w.Header().Set("Content-Type", "application/json")
  54. w.Write([]byte(`{"status":"ok"}`))
  55. }
  56. func getUserAppStatus() (string, string) {
  57. status := noValue
  58. pid := noValue
  59. out, err := exec.Command("systemctl", "show", "yfkj-user-app.service",
  60. "-p", "LoadState",
  61. "-p", "ActiveState",
  62. "-p", "SubState",
  63. "-p", "MainPID").Output()
  64. if err != nil {
  65. return status, pid
  66. }
  67. v := string(out)
  68. switch {
  69. case strings.Contains(v, "ActiveState=active") &&
  70. strings.Contains(v, "SubState=running"):
  71. status = "🟢运行中"
  72. case strings.Contains(v, "ActiveState=active") &&
  73. strings.Contains(v, "SubState=exited"):
  74. status = "🔵已完成"
  75. case strings.Contains(v, "LoadState=loaded") &&
  76. strings.Contains(v, "ActiveState=inactive"):
  77. status = "🟡已停止"
  78. }
  79. for _, line := range strings.Split(v, "\n") {
  80. if after, ok := strings.CutPrefix(line, "MainPID="); ok {
  81. if after != "" && after != "0" {
  82. pid = after
  83. }
  84. break
  85. }
  86. }
  87. return status, pid
  88. }
  89. func appInstallStatusHandler(w http.ResponseWriter, r *http.Request) {
  90. if r.Method != http.MethodGet {
  91. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  92. return
  93. }
  94. var info *struct {
  95. Pkg struct {
  96. FileName string `json:"fileName"`
  97. FileSize int64 `json:"fileSize"`
  98. FileMD5 string `json:"fileMD5"`
  99. UploadTime string `json:"uploadTime"`
  100. } `json:"pkg"`
  101. App struct {
  102. Name string `json:"name"`
  103. Version string `json:"version"`
  104. Executable string `json:"executable"`
  105. Description string `json:"description"`
  106. ReadmeFile string `json:"readmeFile"`
  107. } `json:"app"`
  108. Ins struct {
  109. Installed bool `json:"installed"`
  110. InstallTime string `json:"installTime"`
  111. } `json:"ins"`
  112. }
  113. callErr := callRPCResult(r.Context(), 7004, "core.getInstallInfo", nil, &info)
  114. data := map[string]string{
  115. "state": "0",
  116. "avar1": noValue,
  117. "avar2": noValue,
  118. "avar3": noValue,
  119. "avar4": noValue,
  120. "bvar1": noValue,
  121. "bvar2": noValue,
  122. "bvar3": noValue,
  123. "bvar4": noValue,
  124. "bvar5": noValue,
  125. "cvar1": noValue,
  126. "cvar2": noValue,
  127. "cvar3": noValue,
  128. "cvar4": noValue,
  129. "cvar5": noValue,
  130. }
  131. if callErr == nil {
  132. data["avar1"] = "🟢正常"
  133. if info != nil {
  134. data["bvar1"] = info.Pkg.FileName
  135. data["bvar2"] = fmt.Sprintf("%d 字节", info.Pkg.FileSize)
  136. data["bvar3"] = info.Pkg.FileMD5
  137. data["bvar4"] = info.Pkg.UploadTime
  138. data["cvar1"] = info.App.Name
  139. data["cvar2"] = info.App.Version
  140. data["cvar3"] = info.App.Executable
  141. data["cvar4"] = info.Ins.InstallTime
  142. data["cvar5"] = info.App.ReadmeFile
  143. if info.Ins.Installed {
  144. data["state"] = "2"
  145. data["avar2"] = info.App.Name
  146. data["avar3"], data["avar4"] = getUserAppStatus()
  147. data["bvar5"] = "已安装"
  148. } else {
  149. data["state"] = "1"
  150. data["bvar5"] = "未安装"
  151. }
  152. }
  153. }
  154. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  155. json.NewEncoder(w).Encode(data)
  156. }
  157. func uploadUserAppPkg(w http.ResponseWriter, r *http.Request) {
  158. if r.Method != http.MethodPost {
  159. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  160. return
  161. }
  162. r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
  163. err := r.ParseMultipartForm(maxUploadSize)
  164. if err != nil {
  165. http.Error(w, "解析上传文件失败: "+err.Error(), http.StatusBadRequest)
  166. return
  167. }
  168. file, header, err := r.FormFile("file")
  169. if err != nil {
  170. http.Error(w, "获取上传文件失败: "+err.Error(), http.StatusBadRequest)
  171. return
  172. }
  173. defer file.Close()
  174. if !strings.HasSuffix(strings.ToLower(header.Filename), ".tar.gz") {
  175. http.Error(w, "只允许上传 tar.gz 类型的文件", http.StatusBadRequest)
  176. return
  177. }
  178. path := filepath.Join("/tmp", filepath.Base(header.Filename))
  179. err = saveUploadFile(file, path)
  180. if err != nil {
  181. http.Error(w, "保存上传文件失败: "+err.Error(), http.StatusInternalServerError)
  182. return
  183. }
  184. baseapp.Logger.Infof(
  185. "[上传文件成功] 上传文件名: %s, 上传文件大小: %d 字节, 本地存储路径: %s",
  186. header.Filename, header.Size, path)
  187. w.WriteHeader(http.StatusOK)
  188. }
  189. func saveUploadFile(src io.Reader, path string) error {
  190. dst, err := os.Create(path)
  191. if err != nil {
  192. return err
  193. }
  194. defer dst.Close()
  195. _, err = io.Copy(dst, src)
  196. if err != nil {
  197. os.Remove(path)
  198. return err
  199. }
  200. return nil
  201. }
  202. func appPkgImportHandler(w http.ResponseWriter, r *http.Request) {
  203. if r.Method != http.MethodPost {
  204. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  205. return
  206. }
  207. var params struct {
  208. File string `json:"file"`
  209. }
  210. if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
  211. http.Error(w, err.Error(), http.StatusBadRequest)
  212. return
  213. }
  214. if params.File == "" {
  215. http.Error(w, "missing file", http.StatusBadRequest)
  216. return
  217. }
  218. callErr := callRPCResult(r.Context(), 7004, "core.package.import",
  219. map[string]string{"file": params.File}, nil)
  220. if callErr != nil {
  221. http.Error(w, callErr.Error(), http.StatusInternalServerError)
  222. return
  223. }
  224. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  225. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  226. }
  227. func appPkgRemoveHandler(w http.ResponseWriter, r *http.Request) {
  228. if r.Method != http.MethodPost {
  229. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  230. return
  231. }
  232. callErr := callRPCResult(r.Context(), 7004, "core.package.remove", nil, nil)
  233. if callErr != nil {
  234. http.Error(w, callErr.Error(), http.StatusInternalServerError)
  235. return
  236. }
  237. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  238. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  239. }
  240. func appInstallHandler(w http.ResponseWriter, r *http.Request) {
  241. if r.Method != http.MethodPost {
  242. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  243. return
  244. }
  245. callErr := callRPCResult(r.Context(), 7004, "core.app.install", nil, nil)
  246. if callErr != nil {
  247. http.Error(w, callErr.Error(), http.StatusInternalServerError)
  248. return
  249. }
  250. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  251. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  252. }
  253. func appUninstallHandler(w http.ResponseWriter, r *http.Request) {
  254. if r.Method != http.MethodPost {
  255. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  256. return
  257. }
  258. callErr := callRPCResult(r.Context(), 7004, "core.app.uninstall", nil, nil)
  259. if callErr != nil {
  260. http.Error(w, callErr.Error(), http.StatusInternalServerError)
  261. return
  262. }
  263. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  264. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  265. }
  266. func restartUserAppService(w http.ResponseWriter, r *http.Request) {
  267. if r.Method != http.MethodPost {
  268. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  269. return
  270. }
  271. cmd := exec.Command("systemctl", "restart", "yfkj-user-app.service")
  272. if out, err := cmd.CombinedOutput(); err != nil {
  273. http.Error(w, string(out)+err.Error(), http.StatusInternalServerError)
  274. return
  275. }
  276. w.Header().Set("Content-Type", "application/json")
  277. w.Write([]byte(`{"status":"ok"}`))
  278. }
  279. var userAppPkgDir = "/opt/yfkj/app-install.service/user-app-pkg"
  280. func downloadUserAppPkg(w http.ResponseWriter, r *http.Request) {
  281. if r.Method != http.MethodGet {
  282. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  283. return
  284. }
  285. var info struct {
  286. Pkg struct {
  287. FileName string `json:"fileName"`
  288. } `json:"pkg"`
  289. }
  290. data, err := os.ReadFile(filepath.Join(userAppPkgDir, "appins.json"))
  291. if err != nil {
  292. http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
  293. return
  294. }
  295. if err := json.Unmarshal(data, &info); err != nil {
  296. http.Error(w, err.Error(), http.StatusInternalServerError)
  297. return
  298. }
  299. if info.Pkg.FileName == "" {
  300. http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
  301. return
  302. }
  303. filename := filepath.Base(info.Pkg.FileName)
  304. file := filepath.Join(userAppPkgDir, filename)
  305. stat, err := os.Stat(file)
  306. if err != nil || !stat.Mode().IsRegular() {
  307. http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
  308. return
  309. }
  310. w.Header().Set("Content-Type", "application/gzip")
  311. w.Header().Set("Content-Disposition",
  312. fmt.Sprintf(`attachment; filename="%s"`, filename))
  313. http.ServeFile(w, r, file)
  314. }