浏览代码

web 模块添加 web.devops.srv 包, 管理机器上的诸如 nginx 之类的服务

reghao 3 月之前
父节点
当前提交
7db75a100d

+ 31 - 0
common/src/main/java/cn/reghao/bnt/common/model/FileTree.java

@@ -0,0 +1,31 @@
+package cn.reghao.bnt.common.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2025-12-21 11:58:28
+ */
+@Setter
+@Getter
+public class FileTree {
+    private String pid;
+    private String id;
+    private String type;
+    private String path;
+    private List<FileTree> children;
+    private String label;
+    private String value;
+
+    public FileTree(String type, String path) {
+        this.type = type;
+        this.path = path;
+        this.children = new ArrayList<>();
+        this.label = path;
+        this.value = path;
+    }
+}

+ 79 - 0
web/src/main/java/cn/reghao/bnt/web/devops/srv/controller/NginxController.java

@@ -0,0 +1,79 @@
+package cn.reghao.bnt.web.devops.srv.controller;
+
+import cn.reghao.bnt.common.model.FileTree;
+import cn.reghao.bnt.web.devops.srv.model.dto.NginxConf;
+import cn.reghao.bnt.web.devops.srv.service.FileTreeService;
+import cn.reghao.bnt.web.devops.srv.service.NginxService;
+import cn.reghao.jutil.jdk.web.result.Result;
+import cn.reghao.jutil.web.WebResult;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2025-12-20 23:16:46
+ */
+@Slf4j
+@Tag(name = "Nginx 接口")
+@RestController
+@RequestMapping("/api/devops/srv/nginx")
+public class NginxController {
+    private final NginxService nginxService;
+    private final FileTreeService fileTreeService;
+    private String baseDir = "/etc/nginx";
+
+    public NginxController(NginxService nginxService, FileTreeService fileTreeService) {
+        this.nginxService = nginxService;
+        this.fileTreeService = fileTreeService;
+    }
+
+    @Operation(summary = "", description = "N")
+    @GetMapping(value = "/tree", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getFileTree(@RequestParam(value = "machineId") String machineId) {
+        List<FileTree> list = fileTreeService.getFileTree(baseDir);
+        return WebResult.success(list);
+    }
+
+    @Operation(summary = "", description = "N")
+    @GetMapping(value = "/conf", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getNginxConf(@RequestParam(value = "machineId") String machineId,
+                               @RequestParam(value = "path") String path) {
+        if (path.startsWith(baseDir)) {
+            String content = nginxService.getNginxConf(machineId, path);
+            if (content.isBlank()) {
+                return WebResult.failWithMsg("not text file");
+            } else {
+                return WebResult.success(content);
+            }
+        } else {
+            return WebResult.failWithMsg("path invalid");
+        }
+    }
+
+    @Operation(summary = "", description = "N")
+    @PostMapping(value = "/conf", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String updateNginxConf(@RequestBody @Validated NginxConf nginxConf) {
+        Result result = nginxService.updateNginxConf(nginxConf);
+        return WebResult.result(result);
+    }
+
+    @Operation(summary = "", description = "N")
+    @PostMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String nginxTest(@RequestBody @Validated NginxConf nginxConf) {
+        Result result = nginxService.nginxReload();
+        return WebResult.result(result);
+    }
+
+    @Operation(summary = "", description = "N")
+    @PostMapping(value = "/reload", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String nginxReload(@RequestBody @Validated NginxConf nginxConf) {
+        Result result = nginxService.nginxReload();
+        return WebResult.result(result);
+    }
+}

+ 17 - 0
web/src/main/java/cn/reghao/bnt/web/devops/srv/model/dto/NginxConf.java

@@ -0,0 +1,17 @@
+package cn.reghao.bnt.web.devops.srv.model.dto;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.Getter;
+
+/**
+ * @author reghao
+ * @date 2025-12-20 23:06:15
+ */
+@Getter
+public class NginxConf {
+    private String machineId;
+    @NotBlank(message = "textContent 不能为空")
+    @Size(max = 10000, message = "最大长度不能超过 10000 个字符")
+    private String textContent;
+}

+ 134 - 0
web/src/main/java/cn/reghao/bnt/web/devops/srv/service/FileTreeService.java

@@ -0,0 +1,134 @@
+package cn.reghao.bnt.web.devops.srv.service;
+
+import cn.reghao.bnt.common.model.FileTree;
+import cn.reghao.jutil.jdk.string.IdGenerator;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author reghao
+ * @date 2025-12-21 12:09:20
+ */
+@Service
+public class FileTreeService {
+    private final IdGenerator idGenerator = new IdGenerator(10, "file-id");
+    private final List<FileTree> list = new ArrayList<>();
+
+    void walkDir(Path path, String baseDir) throws IOException {
+        Files.walkFileTree(path, new FileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+                try {
+                    process(dir.toFile(), baseDir);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                try {
+                    process(file.toFile(), baseDir);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    void process(File file, String baseDir) {
+        String absolutePath = file.getAbsolutePath();
+        String path = absolutePath.replace(baseDir, "");
+        path = absolutePath;
+        String type = "file";
+        if (file.isDirectory()) {
+            type = "dir";
+        }
+        list.add(new FileTree(type, path));
+    }
+
+    public List<FileTree> getFileTree(String baseDir) {
+        list.clear();
+        try {
+            walkDir(Path.of(baseDir), baseDir);
+            Map<String, FileTree> map = list.stream()
+                    .collect(Collectors.toMap(FileTree::getPath, v -> v));
+
+            // 按 path 长度降序
+            List<String> list = map.keySet().stream()
+                    .sorted((o1, o2) -> o2.length()-o1.length())
+                    .collect(Collectors.toList());
+
+            // 对于一棵树, 从最下面的节点往上收拢
+            for (String path : list) {
+                if (path.isBlank()) {
+                    map.remove(path);
+                    continue;
+                }
+
+                int idx = path.lastIndexOf("/");
+                String parentPath = path.substring(0, idx);
+                if (parentPath.isBlank()) {
+                    continue;
+                }
+
+                FileTree parent = map.get(parentPath);
+                if (parent == null) {
+                    continue;
+                }
+
+                if (parent.getId() == null) {
+                    String id = idGenerator.getUuid();
+                    parent.setId(id);
+                }
+
+                FileTree fileTree = map.get(path);
+                fileTree.setPid(parent.getId());
+                if (fileTree.getId() == null) {
+                    String id = idGenerator.getUuid();
+                    fileTree.setId(id);
+                }
+
+                parent.getChildren().add(fileTree);
+                map.remove(path);
+            }
+
+            String rootId = idGenerator.getUuid();
+            List<FileTree> tree = new ArrayList<>(map.values());
+            for (FileTree fileTree : tree) {
+                fileTree.setPid(rootId);
+                if (fileTree.getId() == null) {
+                    String id = idGenerator.getUuid();
+                    fileTree.setId(id);
+                }
+            }
+
+            return tree;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return Collections.emptyList();
+    }
+}

+ 64 - 0
web/src/main/java/cn/reghao/bnt/web/devops/srv/service/NginxService.java

@@ -0,0 +1,64 @@
+package cn.reghao.bnt.web.devops.srv.service;
+
+import cn.reghao.bnt.web.devops.srv.model.dto.NginxConf;
+import cn.reghao.jutil.jdk.io.TextFile;
+import cn.reghao.jutil.jdk.shell.ShellExecutor;
+import cn.reghao.jutil.jdk.shell.ShellResult;
+import cn.reghao.jutil.jdk.web.result.Result;
+import cn.reghao.jutil.jdk.web.result.ResultStatus;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+
+/**
+ * @author reghao
+ * @date 2025-12-20 23:14:39
+ */
+@Service
+public class NginxService {
+    private TextFile textFile = new TextFile();
+    private ShellExecutor shellExecutor = new ShellExecutor();
+
+    public String getNginxConf(String machineId, String confPath) {
+        File file = new File(confPath);
+        if (file.exists() && file.isFile()) {
+            String content = textFile.readFile(file.getAbsolutePath());
+            return content;
+        }
+        String filePath = "/etc/nginx/nginx.conf";
+        return "";
+    }
+
+    public Result updateNginxConf(NginxConf nginxConf) {
+        Result result = nginxTest();
+        if (result.getCode() != ResultStatus.SUCCESS.getCode()) {
+            return result;
+        }
+
+        return nginxReload();
+    }
+
+    public Result nginxReload() {
+        String cmd = "/usr/bin/nginx -s reload";
+        ShellResult shellResult = shellExecutor.exec(cmd.split("\\s+"));
+        int exitCode = shellResult.getExitCode();
+        String result = shellResult.getResult();
+        if (exitCode == 0) {
+            return Result.successWithMsg(result);
+        } else {
+            return Result.fail(result);
+        }
+    }
+
+    public Result nginxTest() {
+        String cmd = "/usr/bin/nginx -t";
+        ShellResult shellResult = shellExecutor.exec(cmd.split("\\s+"));
+        int exitCode = shellResult.getExitCode();
+        String result = shellResult.getResult();
+        if (exitCode == 0) {
+            return Result.successWithMsg(result);
+        } else {
+            return Result.fail(result);
+        }
+    }
+}