server.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. package httpserver
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "os"
  9. "strings"
  10. "sync"
  11. "time"
  12. "networktool/internal/auth"
  13. "networktool/internal/config"
  14. "networktool/internal/deviceinfo"
  15. "networktool/internal/logger"
  16. "networktool/internal/model"
  17. applyexecsvc "networktool/internal/network/applyexec"
  18. configreadersvc "networktool/internal/network/configreader"
  19. interfacesvc "networktool/internal/network/interfaces"
  20. netplansvc "networktool/internal/network/netplan"
  21. validatorsvc "networktool/internal/network/validator"
  22. "networktool/internal/systemaction"
  23. "networktool/internal/tasks"
  24. )
  25. type Server struct {
  26. cfg config.Config
  27. log *logger.Logger
  28. deviceSvc *deviceinfo.Service
  29. interfaceSvc *interfacesvc.Service
  30. configSvc *configreadersvc.Service
  31. validatorSvc *validatorsvc.Service
  32. netplanSvc *netplansvc.Service
  33. applySvc *applyexecsvc.Service
  34. taskSvc *tasks.Service
  35. systemSvc *systemaction.Service
  36. confirmMu sync.Mutex
  37. applyControls map[string]applyControl
  38. }
  39. type applyControl struct {
  40. confirm chan struct{}
  41. cancel chan struct{}
  42. }
  43. const applyConfirmationTimeout = 20 * time.Second
  44. func New(cfg config.Config, log *logger.Logger, deviceSvc *deviceinfo.Service, interfaceSvc *interfacesvc.Service, configSvc *configreadersvc.Service, validatorSvc *validatorsvc.Service, netplanSvc *netplansvc.Service, applySvc *applyexecsvc.Service, taskSvc *tasks.Service, systemSvc *systemaction.Service) *Server {
  45. return &Server{cfg: cfg, log: log, deviceSvc: deviceSvc, interfaceSvc: interfaceSvc, configSvc: configSvc, validatorSvc: validatorSvc, netplanSvc: netplanSvc, applySvc: applySvc, taskSvc: taskSvc, systemSvc: systemSvc, applyControls: make(map[string]applyControl)}
  46. }
  47. func (s *Server) Run(ctx context.Context) error {
  48. mux := http.NewServeMux()
  49. mux.Handle("/api/health", auth.Middleware(s.cfg, http.HandlerFunc(s.handleHealth)))
  50. mux.Handle("/api/device/info", auth.Middleware(s.cfg, http.HandlerFunc(s.handleDeviceInfo)))
  51. mux.Handle("/api/network/interfaces", auth.Middleware(s.cfg, http.HandlerFunc(s.handleInterfaces)))
  52. mux.Handle("/api/network/config", auth.Middleware(s.cfg, http.HandlerFunc(s.handleConfig)))
  53. mux.Handle("/api/network/validate", auth.Middleware(s.cfg, http.HandlerFunc(s.handleValidate)))
  54. mux.Handle("/api/network/apply", auth.Middleware(s.cfg, http.HandlerFunc(s.handleApply)))
  55. mux.Handle("/api/network/apply/confirm", auth.Middleware(s.cfg, http.HandlerFunc(s.handleApplyConfirm)))
  56. mux.Handle("/api/network/apply/cancel", auth.Middleware(s.cfg, http.HandlerFunc(s.handleApplyCancel)))
  57. mux.Handle("/api/network/rollback", auth.Middleware(s.cfg, http.HandlerFunc(s.handleRollback)))
  58. mux.Handle("/api/system/reboot", auth.Middleware(s.cfg, http.HandlerFunc(s.handleReboot)))
  59. mux.Handle("/api/system/shutdown", auth.Middleware(s.cfg, http.HandlerFunc(s.handleShutdown)))
  60. mux.Handle("/api/tasks/", auth.Middleware(s.cfg, http.HandlerFunc(s.handleTaskGet)))
  61. handler := s.withAccessLog(mux)
  62. server := &http.Server{
  63. Addr: fmt.Sprintf("%s:%d", s.cfg.HTTPHost, s.cfg.HTTPPort),
  64. Handler: handler,
  65. ReadHeaderTimeout: 5 * time.Second,
  66. }
  67. go func() {
  68. <-ctx.Done()
  69. shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  70. defer cancel()
  71. _ = server.Shutdown(shutdownCtx)
  72. }()
  73. s.log.Info("http server listening", "addr", server.Addr)
  74. err := server.ListenAndServe()
  75. if err == http.ErrServerClosed {
  76. return nil
  77. }
  78. return err
  79. }
  80. func (s *Server) handleApplyConfirm(w http.ResponseWriter, r *http.Request) {
  81. if r.Method != http.MethodPost {
  82. writeJSON(w, http.StatusMethodNotAllowed, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  83. return
  84. }
  85. var input model.ConfirmApplyRequest
  86. if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
  87. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"请求体格式不正确。"}}})
  88. return
  89. }
  90. if input.TaskID == "" {
  91. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"缺少 task_id。"}}})
  92. return
  93. }
  94. if !s.confirmApply(input.TaskID) {
  95. writeJSON(w, http.StatusNotFound, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  96. return
  97. }
  98. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "已确认保留配置", Data: map[string]any{"task_id": input.TaskID, "confirmed": true}})
  99. }
  100. func (s *Server) handleApplyCancel(w http.ResponseWriter, r *http.Request) {
  101. if r.Method != http.MethodPost {
  102. writeJSON(w, http.StatusMethodNotAllowed, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  103. return
  104. }
  105. var input model.ConfirmApplyRequest
  106. if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
  107. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"请求体格式不正确。"}}})
  108. return
  109. }
  110. if input.TaskID == "" {
  111. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"缺少 task_id。"}}})
  112. return
  113. }
  114. if !s.cancelApply(input.TaskID) {
  115. writeJSON(w, http.StatusNotFound, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  116. return
  117. }
  118. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "已取消保留配置,正在回滚", Data: map[string]any{"task_id": input.TaskID, "cancelled": true}})
  119. }
  120. func (s *Server) withAccessLog(next http.Handler) http.Handler {
  121. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  122. started := time.Now()
  123. rw := &statusRecorder{ResponseWriter: w, statusCode: http.StatusOK}
  124. next.ServeHTTP(rw, r)
  125. s.log.Info(
  126. "http request completed",
  127. "method", r.Method,
  128. "path", r.URL.Path,
  129. "query", r.URL.RawQuery,
  130. "remote", r.RemoteAddr,
  131. "status", rw.statusCode,
  132. "duration_ms", time.Since(started).Milliseconds(),
  133. )
  134. })
  135. }
  136. type statusRecorder struct {
  137. http.ResponseWriter
  138. statusCode int
  139. }
  140. func (r *statusRecorder) WriteHeader(statusCode int) {
  141. r.statusCode = statusCode
  142. r.ResponseWriter.WriteHeader(statusCode)
  143. }
  144. func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) {
  145. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "成功", Data: map[string]any{"status": "运行中", "server_version": s.cfg.ServerVersion}})
  146. }
  147. func (s *Server) handleDeviceInfo(w http.ResponseWriter, _ *http.Request) {
  148. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "成功", Data: s.deviceSvc.Get()})
  149. }
  150. func (s *Server) handleInterfaces(w http.ResponseWriter, _ *http.Request) {
  151. data, err := s.interfaceSvc.List()
  152. if err != nil {
  153. writeJSON(w, http.StatusInternalServerError, model.APIResponse{Code: 4001, Message: "系统执行失败", Data: map[string]string{"error": err.Error()}})
  154. return
  155. }
  156. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "成功", Data: data})
  157. }
  158. func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
  159. interfaceName := r.URL.Query().Get("interface")
  160. if interfaceName == "" {
  161. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"缺少 interface 参数。"}}})
  162. return
  163. }
  164. if !s.interfaceExists(interfaceName) {
  165. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"目标接口不存在。"}}})
  166. return
  167. }
  168. data, err := s.configSvc.Read(interfaceName)
  169. if err != nil {
  170. writeJSON(w, http.StatusInternalServerError, model.APIResponse{Code: 4001, Message: "系统执行失败", Data: map[string]string{"error": err.Error()}})
  171. return
  172. }
  173. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "成功", Data: data})
  174. }
  175. func (s *Server) handleValidate(w http.ResponseWriter, r *http.Request) {
  176. if r.Method != http.MethodPost {
  177. writeJSON(w, http.StatusMethodNotAllowed, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  178. return
  179. }
  180. body, err := io.ReadAll(r.Body)
  181. if err != nil {
  182. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"请求体读取失败。"}}})
  183. return
  184. }
  185. var input model.InterfaceConfig
  186. if err := json.Unmarshal(body, &input); err != nil {
  187. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"请求体格式不正确。"}}})
  188. return
  189. }
  190. if input.Interface != "" && !s.interfaceExists(input.Interface) {
  191. result := model.ValidateResponse{Valid: false, Errors: []string{"目标接口不存在。"}, Warnings: []string{}}
  192. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 3001, Message: "配置校验失败", Data: result})
  193. return
  194. }
  195. result := s.validatorSvc.Validate(input)
  196. if !result.Valid {
  197. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 3001, Message: "配置校验失败", Data: result})
  198. return
  199. }
  200. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "校验通过", Data: result})
  201. }
  202. func (s *Server) handleApply(w http.ResponseWriter, r *http.Request) {
  203. if r.Method != http.MethodPost {
  204. writeJSON(w, http.StatusMethodNotAllowed, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  205. return
  206. }
  207. if !hasRootPrivileges() {
  208. writeJSON(w, http.StatusForbidden, model.APIResponse{Code: 4001, Message: "系统执行失败", Data: map[string][]string{"errors": []string{"Server 未以 root 身份运行,无法写入 netplan 或执行 netplan apply。"}}})
  209. return
  210. }
  211. var input model.InterfaceConfig
  212. if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
  213. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"请求体格式不正确。"}}})
  214. return
  215. }
  216. if !s.interfaceExists(input.Interface) {
  217. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 3001, Message: "配置校验失败", Data: model.ValidateResponse{Valid: false, Errors: []string{"目标接口不存在。"}}})
  218. return
  219. }
  220. result := s.validatorSvc.Validate(input)
  221. if !result.Valid {
  222. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 3001, Message: "配置校验失败", Data: result})
  223. return
  224. }
  225. management := s.currentManagementInterface()
  226. if management == "" {
  227. writeJSON(w, http.StatusInternalServerError, model.APIResponse{Code: 4001, Message: "系统执行失败", Data: map[string][]string{"errors": []string{"未能识别管理接口。"}}})
  228. return
  229. }
  230. task := s.taskSvc.Create()
  231. go s.runApplyTask(task.TaskID, input, management)
  232. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "配置任务已提交", Data: map[string]any{"interface": input.Interface, "task_id": task.TaskID}})
  233. }
  234. func (s *Server) handleRollback(w http.ResponseWriter, r *http.Request) {
  235. if r.Method != http.MethodPost {
  236. writeJSON(w, http.StatusMethodNotAllowed, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  237. return
  238. }
  239. if !hasRootPrivileges() {
  240. writeJSON(w, http.StatusForbidden, model.APIResponse{Code: 4001, Message: "系统执行失败", Data: map[string][]string{"errors": []string{"Server 未以 root 身份运行,无法恢复 netplan 或执行 netplan apply。"}}})
  241. return
  242. }
  243. var input model.RollbackRequest
  244. if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
  245. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"请求体格式不正确。"}}})
  246. return
  247. }
  248. filePath, err := s.netplanSvc.FindSingleFile()
  249. if err != nil {
  250. writeJSON(w, http.StatusInternalServerError, model.APIResponse{Code: 4001, Message: "系统执行失败", Data: map[string]string{"error": err.Error()}})
  251. return
  252. }
  253. backupPath := filePath + ".networktool.bak"
  254. if err := s.netplanSvc.Restore(filePath, backupPath); err != nil {
  255. writeJSON(w, http.StatusInternalServerError, model.APIResponse{Code: 3004, Message: "回滚失败", Data: map[string]string{"error": err.Error()}})
  256. return
  257. }
  258. if err := s.applySvc.Apply(); err != nil {
  259. writeJSON(w, http.StatusInternalServerError, model.APIResponse{Code: 3004, Message: "回滚失败", Data: map[string]string{"error": err.Error()}})
  260. return
  261. }
  262. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "回滚成功", Data: map[string]any{"interface": input.Interface, "rolled_back": true}})
  263. }
  264. func (s *Server) handleTaskGet(w http.ResponseWriter, r *http.Request) {
  265. taskID := strings.TrimPrefix(r.URL.Path, "/api/tasks/")
  266. if taskID == "" {
  267. writeJSON(w, http.StatusBadRequest, model.APIResponse{Code: 2001, Message: "参数错误", Data: map[string][]string{"errors": []string{"缺少 task_id。"}}})
  268. return
  269. }
  270. item, ok := s.taskSvc.Get(taskID)
  271. if !ok {
  272. writeJSON(w, http.StatusNotFound, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  273. return
  274. }
  275. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: "成功", Data: item})
  276. }
  277. func (s *Server) handleReboot(w http.ResponseWriter, r *http.Request) {
  278. s.handleSystemAction(w, r, "reboot")
  279. }
  280. func (s *Server) handleShutdown(w http.ResponseWriter, r *http.Request) {
  281. s.handleSystemAction(w, r, "shutdown")
  282. }
  283. func (s *Server) handleSystemAction(w http.ResponseWriter, r *http.Request, action string) {
  284. if r.Method != http.MethodPost {
  285. writeJSON(w, http.StatusMethodNotAllowed, model.APIResponse{Code: 2002, Message: "资源不存在", Data: nil})
  286. return
  287. }
  288. if !hasRootPrivileges() {
  289. writeJSON(w, http.StatusForbidden, model.APIResponse{Code: 4001, Message: "系统执行失败", Data: map[string][]string{"errors": []string{"Server 未以 root 身份运行,无法执行重启或关机。"}}})
  290. return
  291. }
  292. task := s.taskSvc.Create()
  293. go s.runSystemTask(task.TaskID, action)
  294. message := "系统任务已提交"
  295. if action == "reboot" {
  296. message = "重启任务已提交"
  297. } else if action == "shutdown" {
  298. message = "关机任务已提交"
  299. }
  300. writeJSON(w, http.StatusOK, model.APIResponse{Code: 0, Message: message, Data: map[string]any{"action": action, "task_id": task.TaskID}})
  301. }
  302. func (s *Server) runApplyTask(taskID string, input model.InterfaceConfig, managementInterface string) {
  303. s.taskSvc.Update(taskID, "running", "validating", "正在校验配置。", false)
  304. result := s.validatorSvc.Validate(input)
  305. if !result.Valid {
  306. s.taskSvc.Update(taskID, "failed", "validating", "配置校验失败。", false)
  307. return
  308. }
  309. filePath, err := s.netplanSvc.FindSingleFile()
  310. if err != nil {
  311. s.taskSvc.Update(taskID, "failed", "writing_netplan", err.Error(), false)
  312. return
  313. }
  314. s.taskSvc.Update(taskID, "running", "writing_netplan", "正在写入 netplan 配置。", false)
  315. backupPath, err := s.netplanSvc.Backup(filePath)
  316. if err != nil {
  317. s.taskSvc.Update(taskID, "failed", "writing_netplan", err.Error(), false)
  318. return
  319. }
  320. s.log.Info("preparing to write netplan", "task_id", taskID, "file", filePath, "target_interface", input.Interface, "ip", input.IP, "prefix", input.Prefix, "gateway", input.Gateway, "dns", strings.Join(input.DNS, ","))
  321. if err := s.netplanSvc.Write(filePath, input.Interface, input, managementInterface, s.cfg.MaintenanceCIDR); err != nil {
  322. s.taskSvc.Update(taskID, "failed", "writing_netplan", err.Error(), false)
  323. return
  324. }
  325. if writtenData, err := os.ReadFile(filePath); err != nil {
  326. s.log.Error("failed to read netplan after write", "task_id", taskID, "file", filePath, "error", err.Error())
  327. } else {
  328. s.log.Info("netplan written", "task_id", taskID, "file", filePath, "content", string(writtenData))
  329. }
  330. control := s.registerApplyControl(taskID)
  331. defer s.unregisterApplyControl(taskID)
  332. s.taskSvc.Update(taskID, "running", "applying", "正在应用 netplan 配置。", false)
  333. if err := s.applySvc.Apply(); err != nil {
  334. s.log.Error("netplan apply failed, restoring netplan file", "task_id", taskID, "file", filePath, "error", err.Error())
  335. _ = s.netplanSvc.Restore(filePath, backupPath)
  336. _ = s.applySvc.Apply()
  337. _ = s.interfaceSvc.EnsureMaintenanceAddress()
  338. s.logNetplanFile(taskID, filePath, "netplan restored after apply failure")
  339. s.taskSvc.Update(taskID, "rolled_back", "rolling_back", fmt.Sprintf("应用 netplan 失败,已自动回滚:%v", err), true)
  340. return
  341. }
  342. s.taskSvc.Update(taskID, "running", "confirming", fmt.Sprintf("配置已应用,请在 %d 秒内确认保留;未确认将自动回滚。", int(applyConfirmationTimeout.Seconds())), false)
  343. select {
  344. case <-control.confirm:
  345. _ = os.Remove(backupPath)
  346. s.taskSvc.Update(taskID, "success", "completed", "配置已应用并由客户端确认保留。", false)
  347. return
  348. case <-control.cancel:
  349. s.rollbackAppliedConfig(taskID, filePath, backupPath, "用户取消保留配置")
  350. return
  351. case <-time.After(applyConfirmationTimeout):
  352. s.rollbackAppliedConfig(taskID, filePath, backupPath, "确认超时")
  353. return
  354. }
  355. }
  356. func (s *Server) rollbackAppliedConfig(taskID string, filePath string, backupPath string, reason string) {
  357. s.log.Warn("apply confirmation failed, restoring netplan file", "task_id", taskID, "file", filePath, "reason", reason)
  358. _ = s.netplanSvc.Restore(filePath, backupPath)
  359. _ = s.applySvc.Apply()
  360. _ = s.interfaceSvc.EnsureMaintenanceAddress()
  361. s.logNetplanFile(taskID, filePath, "netplan restored after confirmation failure")
  362. s.taskSvc.Update(taskID, "rolled_back", "rolling_back", fmt.Sprintf("%s,已自动回滚。", reason), true)
  363. }
  364. func (s *Server) registerApplyControl(taskID string) applyControl {
  365. control := applyControl{confirm: make(chan struct{}, 1), cancel: make(chan struct{}, 1)}
  366. s.confirmMu.Lock()
  367. s.applyControls[taskID] = control
  368. s.confirmMu.Unlock()
  369. return control
  370. }
  371. func (s *Server) unregisterApplyControl(taskID string) {
  372. s.confirmMu.Lock()
  373. delete(s.applyControls, taskID)
  374. s.confirmMu.Unlock()
  375. }
  376. func (s *Server) confirmApply(taskID string) bool {
  377. s.confirmMu.Lock()
  378. control, ok := s.applyControls[taskID]
  379. s.confirmMu.Unlock()
  380. if !ok {
  381. return false
  382. }
  383. select {
  384. case control.confirm <- struct{}{}:
  385. default:
  386. }
  387. return true
  388. }
  389. func (s *Server) cancelApply(taskID string) bool {
  390. s.confirmMu.Lock()
  391. control, ok := s.applyControls[taskID]
  392. s.confirmMu.Unlock()
  393. if !ok {
  394. return false
  395. }
  396. select {
  397. case control.cancel <- struct{}{}:
  398. default:
  399. }
  400. return true
  401. }
  402. func (s *Server) logNetplanFile(taskID string, filePath string, message string) {
  403. data, err := os.ReadFile(filePath)
  404. if err != nil {
  405. s.log.Error("failed to read netplan file for logging", "task_id", taskID, "file", filePath, "error", err.Error())
  406. return
  407. }
  408. s.log.Info(message, "task_id", taskID, "file", filePath, "content", string(data))
  409. }
  410. func (s *Server) runSystemTask(taskID string, action string) {
  411. detail := "正在发送系统指令。"
  412. if action == "reboot" {
  413. detail = "正在发送重启指令。"
  414. } else if action == "shutdown" {
  415. detail = "正在发送关机指令。"
  416. }
  417. s.taskSvc.Update(taskID, "running", "executing", detail, false)
  418. var err error
  419. switch action {
  420. case "reboot":
  421. err = s.systemSvc.Reboot()
  422. case "shutdown":
  423. err = s.systemSvc.Shutdown()
  424. default:
  425. err = fmt.Errorf("unsupported system action: %s", action)
  426. }
  427. if err != nil {
  428. s.log.Error("system action failed", "action", action, "error", err.Error())
  429. s.taskSvc.Update(taskID, "failed", "executing", err.Error(), false)
  430. return
  431. }
  432. completedDetail := "系统指令已发送。"
  433. if action == "reboot" {
  434. completedDetail = "系统重启指令已发送。"
  435. } else if action == "shutdown" {
  436. completedDetail = "系统关机指令已发送。"
  437. }
  438. s.taskSvc.Update(taskID, "success", "completed", completedDetail, false)
  439. }
  440. func (s *Server) interfaceExists(name string) bool {
  441. data, err := s.interfaceSvc.List()
  442. if err != nil {
  443. return false
  444. }
  445. for _, item := range data.Interfaces {
  446. if item.SystemName == name {
  447. return true
  448. }
  449. }
  450. return false
  451. }
  452. func (s *Server) currentManagementInterface() string {
  453. data, err := s.interfaceSvc.List()
  454. if err != nil {
  455. return ""
  456. }
  457. return data.ManagementInterface
  458. }
  459. func writeJSON(w http.ResponseWriter, status int, payload model.APIResponse) {
  460. w.Header().Set("Content-Type", "application/json")
  461. w.WriteHeader(status)
  462. _ = json.NewEncoder(w).Encode(payload)
  463. }
  464. func hasRootPrivileges() bool {
  465. return os.Geteuid() == 0
  466. }