web_handler.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. package main
  2. import (
  3. "bufio"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "io/fs"
  8. "net/http"
  9. "os"
  10. "os/exec"
  11. "strings"
  12. "hnyfkj.com.cn/rtu/linux/baseapp"
  13. "hnyfkj.com.cn/rtu/linux/utils/jsonrpc2"
  14. )
  15. const noValue = "--"
  16. type LoginReq struct {
  17. Username string `json:"username"`
  18. Password string `json:"password"`
  19. }
  20. type LoginResp struct {
  21. Success bool `json:"success"`
  22. }
  23. func loginHandler(w http.ResponseWriter, r *http.Request) {
  24. if r.Method != http.MethodPost {
  25. w.WriteHeader(http.StatusMethodNotAllowed)
  26. return
  27. }
  28. var req LoginReq
  29. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  30. w.WriteHeader(http.StatusBadRequest)
  31. return
  32. }
  33. ok := req.Username == "admin" && req.Password == "admin123456"
  34. if ok {
  35. sessionID := createSession()
  36. addSession(sessionID)
  37. http.SetCookie(w, &http.Cookie{
  38. Name: "session_id",
  39. Value: sessionID,
  40. Path: "/",
  41. HttpOnly: true,
  42. SameSite: http.SameSiteStrictMode,
  43. })
  44. }
  45. w.Header().Set("Content-Type", "application/json")
  46. json.NewEncoder(w).Encode(LoginResp{Success: ok})
  47. }
  48. func logoutHandler(w http.ResponseWriter, r *http.Request) {
  49. if cookie, err := r.Cookie("session_id"); err == nil {
  50. sessionMu.Lock()
  51. delete(sessions, cookie.Value)
  52. sessionMu.Unlock()
  53. }
  54. http.SetCookie(w, &http.Cookie{
  55. Name: "session_id",
  56. Value: "",
  57. Path: "/",
  58. MaxAge: -1,
  59. })
  60. http.Redirect(w, r, "/", http.StatusFound)
  61. }
  62. func getIMEIHandler(w http.ResponseWriter, r *http.Request) {
  63. data, err := os.ReadFile("/var/device_imei.txt")
  64. if err != nil {
  65. w.Write([]byte(noValue))
  66. } else {
  67. w.Write(append([]byte("🆔"), data...))
  68. }
  69. }
  70. func (ui *WebUI) rootHandler(w http.ResponseWriter, r *http.Request) {
  71. path := r.URL.Path
  72. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  73. w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
  74. w.Header().Set("Pragma", "no-cache")
  75. w.Header().Set("Expires", "0")
  76. // 1, 未登录时, 只能看登录页
  77. if !checkSession(r) {
  78. data, err := fs.ReadFile(ui.fs, "login.html")
  79. if err != nil {
  80. http.NotFound(w, r)
  81. } else {
  82. w.Write(data)
  83. }
  84. return
  85. }
  86. // 2, 登录成功, 展示应用首页
  87. if path == "/" {
  88. data, err := fs.ReadFile(ui.fs, "app.html")
  89. if err != nil {
  90. http.NotFound(w, r)
  91. } else {
  92. w.Write(data)
  93. }
  94. return
  95. }
  96. // 3, 在网站内跳转其它功能页
  97. if strings.HasPrefix(path, "/pages/") { // path 形如 "/pages/page1.html"
  98. data, err := fs.ReadFile(ui.fs, path[1:])
  99. if err != nil {
  100. http.NotFound(w, r)
  101. } else {
  102. w.Write(data)
  103. }
  104. return
  105. }
  106. // 4, 访问其它不认识的页面时
  107. http.NotFound(w, r)
  108. }
  109. func serviceLogHandler(w http.ResponseWriter, r *http.Request) {
  110. unit := r.URL.Query().Get("unit")
  111. if unit == "" {
  112. http.Error(w, "missing unit",
  113. http.StatusBadRequest)
  114. return
  115. }
  116. w.Header().Set("Content-Type", "text/event-stream")
  117. w.Header().Set("Cache-Control", "no-cache")
  118. w.Header().Set("Connection", "keep-alive")
  119. w.Header().Set("X-Accel-Buffering", "no")
  120. flusher, ok := w.(http.Flusher)
  121. if !ok {
  122. http.Error(w, "not supported",
  123. http.StatusInternalServerError)
  124. return
  125. }
  126. cmd := exec.Command(
  127. "journalctl",
  128. "-f",
  129. "-u",
  130. unit,
  131. "-n",
  132. "10",
  133. "--no-pager",
  134. "-q",
  135. "-o",
  136. "short-iso",
  137. )
  138. stdout, err := cmd.StdoutPipe()
  139. if err != nil {
  140. http.Error(w, err.Error(), http.StatusInternalServerError)
  141. return
  142. }
  143. if err := cmd.Start(); err != nil {
  144. http.Error(w, err.Error(), http.StatusInternalServerError)
  145. return
  146. }
  147. if true {
  148. baseapp.Logger.Debugf("[服务日志流启动] unit=%s", unit)
  149. }
  150. defer func() {
  151. if cmd.Process != nil {
  152. cmd.Process.Kill()
  153. }
  154. cmd.Wait()
  155. baseapp.Logger.Debugf("[服务日志流关闭] unit=%s", unit)
  156. }()
  157. scanner := bufio.NewScanner(stdout)
  158. for scanner.Scan() {
  159. select {
  160. case <-r.Context().Done():
  161. return
  162. default:
  163. }
  164. if _, err := fmt.Fprintf(w, "data: %s\n\n", scanner.Text()); err != nil {
  165. return
  166. }
  167. flusher.Flush()
  168. }
  169. }
  170. func runShellAndWrite(w http.ResponseWriter, cmd string) {
  171. baseapp.Logger.Debugf("[执行命令] %s", cmd)
  172. out, err := exec.Command("sh", "-c", cmd).CombinedOutput()
  173. if err != nil {
  174. baseapp.Logger.Errorf("[命令失败] %s err=%v", cmd, err)
  175. http.Error(w, err.Error()+"\n"+string(out),
  176. http.StatusInternalServerError)
  177. return
  178. }
  179. w.Write(out)
  180. }
  181. func netInterfaces(w http.ResponseWriter, r *http.Request) {
  182. runShellAndWrite(w, "ifconfig")
  183. }
  184. func netRoutes(w http.ResponseWriter, r *http.Request) {
  185. runShellAndWrite(w, "route -n")
  186. }
  187. func netDNS(w http.ResponseWriter, r *http.Request) {
  188. runShellAndWrite(w, "cat /etc/resolv.conf")
  189. }
  190. func callRPCResult(ctx context.Context, port int, method string, params any, result any) error {
  191. url := fmt.Sprintf("http://127.0.0.1:%d/rpc", port)
  192. req, _ := json.Marshal(params)
  193. baseapp.Logger.Debugf("[接收RPC请求] %s %s params=%s\n", url, method, string(req))
  194. client, err := jsonrpc2.NewRPCClient(url)
  195. if err != nil {
  196. baseapp.Logger.Errorf("[执行RPC错误] %s err=%v\n", method, err)
  197. return err
  198. }
  199. resp, err := client.Call(ctx, method, params)
  200. if err != nil {
  201. return err
  202. }
  203. baseapp.Logger.Debugf("[发送RPC应答] %s result=%s\n", method, string(resp.Result))
  204. return json.Unmarshal(resp.Result, result)
  205. }
  206. func callRPCResponse(w http.ResponseWriter, r *http.Request, port int, method string, params any) {
  207. url := fmt.Sprintf("http://127.0.0.1:%d/rpc", port)
  208. req, _ := json.Marshal(params)
  209. baseapp.Logger.Debugf("[接收RPC请求] %s %s params=%s\n", url, method, string(req))
  210. client, err := jsonrpc2.NewRPCClient(url)
  211. if err != nil {
  212. http.Error(w, err.Error(), http.StatusInternalServerError)
  213. return
  214. }
  215. resp, err := client.Call(r.Context(), method, params)
  216. if err != nil {
  217. baseapp.Logger.Errorf("[执行RPC错误] %s err=%v\n", method, err)
  218. http.Error(w, err.Error(), http.StatusInternalServerError)
  219. return
  220. }
  221. baseapp.Logger.Debugf("[发送RPC应答] %s result=%s\n", method, string(resp.Result))
  222. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  223. json.NewEncoder(w).Encode(resp.Result)
  224. }
  225. func serviceLogLevel(w http.ResponseWriter, r *http.Request, port int) {
  226. switch r.Method {
  227. case http.MethodGet:
  228. callRPCResponse(w, r, port, "basic.getLogLevel", nil)
  229. case http.MethodPost:
  230. var req struct {
  231. Level string `json:"level"`
  232. }
  233. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  234. http.Error(w, err.Error(), http.StatusBadRequest)
  235. return
  236. }
  237. callRPCResponse(w, r, port, "basic.setLogLevel", map[string]string{"log_level": req.Level})
  238. default:
  239. http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
  240. }
  241. }
  242. func serviceLogLevelHandler(port int) http.HandlerFunc {
  243. return func(w http.ResponseWriter, r *http.Request) {
  244. serviceLogLevel(w, r, port)
  245. }
  246. }