page6_handler.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. mr, err := r.MultipartReader()
  164. if err != nil {
  165. http.Error(w, "解析上传请求失败: "+err.Error(), http.StatusBadRequest)
  166. return
  167. }
  168. for {
  169. part, err := mr.NextPart()
  170. if err == io.EOF {
  171. break
  172. }
  173. if err != nil {
  174. http.Error(w, "读取上传数据失败: "+err.Error(), http.StatusBadRequest)
  175. return
  176. }
  177. if part.FormName() != "file" {
  178. part.Close()
  179. continue
  180. }
  181. filename := filepath.Base(part.FileName())
  182. if filename == "" || filename == "." {
  183. part.Close()
  184. http.Error(w, "文件名为空", http.StatusBadRequest)
  185. return
  186. }
  187. if !strings.HasSuffix(strings.ToLower(filename), ".tar.gz") {
  188. part.Close()
  189. http.Error(w, "只允许上传 tar.gz 类型的文件", http.StatusBadRequest)
  190. return
  191. }
  192. path := filepath.Join("/tmp", filename)
  193. err = saveUploadFile(part, path)
  194. part.Close()
  195. if err != nil {
  196. http.Error(w, "保存上传文件失败: "+err.Error(), http.StatusInternalServerError)
  197. return
  198. }
  199. size := int64(0)
  200. if stat, err := os.Stat(path); err == nil {
  201. size = stat.Size()
  202. }
  203. baseapp.Logger.Infof(
  204. "[上传文件成功] 上传文件名: %s, 上传文件大小: %d 字节, 本地存储路径: %s",
  205. filename, size, path)
  206. break
  207. }
  208. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  209. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  210. }
  211. func saveUploadFile(src io.Reader, path string) error {
  212. dst, err := os.Create(path)
  213. if err != nil {
  214. return err
  215. }
  216. defer dst.Close()
  217. _, err = io.Copy(dst, src)
  218. if err != nil {
  219. os.Remove(path)
  220. return err
  221. }
  222. return nil
  223. }
  224. func appPkgImportHandler(w http.ResponseWriter, r *http.Request) {
  225. if r.Method != http.MethodPost {
  226. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  227. return
  228. }
  229. var params struct {
  230. File string `json:"file"`
  231. }
  232. if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
  233. http.Error(w, err.Error(), http.StatusBadRequest)
  234. return
  235. }
  236. if params.File == "" {
  237. http.Error(w, "missing file", http.StatusBadRequest)
  238. return
  239. }
  240. callErr := callRPCResult(r.Context(), 7004, "core.package.import",
  241. map[string]string{"file": params.File}, nil)
  242. if callErr != nil {
  243. http.Error(w, callErr.Error(), http.StatusInternalServerError)
  244. return
  245. }
  246. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  247. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  248. }
  249. func appPkgRemoveHandler(w http.ResponseWriter, r *http.Request) {
  250. if r.Method != http.MethodPost {
  251. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  252. return
  253. }
  254. callErr := callRPCResult(r.Context(), 7004, "core.package.remove", nil, nil)
  255. if callErr != nil {
  256. http.Error(w, callErr.Error(), http.StatusInternalServerError)
  257. return
  258. }
  259. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  260. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  261. }
  262. func appInstallHandler(w http.ResponseWriter, r *http.Request) {
  263. if r.Method != http.MethodPost {
  264. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  265. return
  266. }
  267. callErr := callRPCResult(r.Context(), 7004, "core.app.install", nil, nil)
  268. if callErr != nil {
  269. http.Error(w, callErr.Error(), http.StatusInternalServerError)
  270. return
  271. }
  272. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  273. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  274. }
  275. func appUninstallHandler(w http.ResponseWriter, r *http.Request) {
  276. if r.Method != http.MethodPost {
  277. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  278. return
  279. }
  280. callErr := callRPCResult(r.Context(), 7004, "core.app.uninstall", nil, nil)
  281. if callErr != nil {
  282. http.Error(w, callErr.Error(), http.StatusInternalServerError)
  283. return
  284. }
  285. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  286. json.NewEncoder(w).Encode(map[string]string{"result": "success"})
  287. }
  288. func restartUserAppService(w http.ResponseWriter, r *http.Request) {
  289. if r.Method != http.MethodPost {
  290. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  291. return
  292. }
  293. cmd := exec.Command("systemctl", "restart", "yfkj-user-app.service")
  294. if out, err := cmd.CombinedOutput(); err != nil {
  295. http.Error(w, string(out)+err.Error(), http.StatusInternalServerError)
  296. return
  297. }
  298. w.Header().Set("Content-Type", "application/json")
  299. w.Write([]byte(`{"status":"ok"}`))
  300. }
  301. var userAppPkgDir = "/opt/yfkj/app-install.service/user-app-pkg"
  302. func downloadUserAppPkg(w http.ResponseWriter, r *http.Request) {
  303. if r.Method != http.MethodGet {
  304. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  305. return
  306. }
  307. var info struct {
  308. Pkg struct {
  309. FileName string `json:"fileName"`
  310. } `json:"pkg"`
  311. }
  312. data, err := os.ReadFile(filepath.Join(userAppPkgDir, "appins.json"))
  313. if err != nil {
  314. http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
  315. return
  316. }
  317. if err := json.Unmarshal(data, &info); err != nil {
  318. http.Error(w, err.Error(), http.StatusInternalServerError)
  319. return
  320. }
  321. if info.Pkg.FileName == "" {
  322. http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
  323. return
  324. }
  325. filename := filepath.Base(info.Pkg.FileName)
  326. file := filepath.Join(userAppPkgDir, filename)
  327. stat, err := os.Stat(file)
  328. if err != nil || !stat.Mode().IsRegular() {
  329. http.Error(w, "用户应用安装包不存在", http.StatusNotFound)
  330. return
  331. }
  332. w.Header().Set("Content-Type", "application/gzip")
  333. w.Header().Set("Content-Disposition",
  334. fmt.Sprintf(`attachment; filename="%s"`, filename))
  335. http.ServeFile(w, r, file)
  336. }