本地 RAG 知识库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

110 lines
3.9 KiB

package com.wok.supportbot.controller;
import com.wok.supportbot.service.LogViewerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.util.List;
import java.util.Map;
/**
* 系统日志查看接口
* <p>
* 提供日志文件列表、尾部内容查看、关键词搜索及文件下载功能。
* 仅 admin 角色可访问,所有文件操作限制在日志目录内,防止路径遍历攻击。
*/
@Slf4j
@RestController
@RequestMapping("/log")
@PreAuthorize("hasRole('admin')")
public class LogController {
@Autowired
private LogViewerService logViewerService;
/**
* 列出所有日志文件(名称、大小、修改时间等)
*/
@GetMapping("/files")
public ResponseEntity<Map<String, Object>> listFiles() {
try {
List<Map<String, Object>> files = logViewerService.listFiles();
return ResponseEntity.ok(Map.of(
"success", true,
"data", files
));
} catch (Exception e) {
log.error("获取日志文件列表失败", e);
return ResponseEntity.status(500).body(Map.of(
"success", false,
"message", "获取日志文件列表失败:" + e.getMessage()
));
}
}
/**
* 查看日志文件尾部内容
*
* @param fileName 日志文件名
* @param lines 读取最后 N 行(默认 200,上限 5000)
* @param keyword 可选搜索关键词
*/
@GetMapping("/view/{fileName}")
public ResponseEntity<Map<String, Object>> viewLog(
@PathVariable String fileName,
@RequestParam(defaultValue = "200") int lines,
@RequestParam(required = false) String keyword) {
try {
Map<String, Object> result = logViewerService.readLogTail(fileName, lines, keyword);
return ResponseEntity.ok(Map.of(
"success", true,
"data", result
));
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
} catch (Exception e) {
log.error("查看日志失败: {}", fileName, e);
return ResponseEntity.status(500).body(Map.of(
"success", false,
"message", "查看日志失败:" + e.getMessage()
));
}
}
/**
* 下载日志文件(原始文件流)
*
* @param fileName 日志文件名
*/
@GetMapping("/download/{fileName}")
public ResponseEntity<Resource> downloadLog(@PathVariable String fileName) {
try {
java.nio.file.Path filePath = logViewerService.resolveSafe(fileName);
File file = filePath.toFile();
FileSystemResource resource = new FileSystemResource(file);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getName() + "\"")
.contentType(MediaType.TEXT_PLAIN)
.contentLength(file.length())
.body(resource);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
log.error("下载日志失败: {}", fileName, e);
return ResponseEntity.status(500).build();
}
}
}