197 lines
4.4 KiB
Go
197 lines
4.4 KiB
Go
package api
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
var upgrader = websocket.Upgrader{
|
|
CheckOrigin: func(r *http.Request) bool { return true },
|
|
}
|
|
|
|
func (h *Handler) StreamLogs(c *gin.Context) {
|
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
|
if err != nil {
|
|
log.Printf("websocket upgrade error: %v", err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
clientID := conn.RemoteAddr().String()
|
|
ch := h.streamer.Subscribe("server", clientID)
|
|
defer h.streamer.Unsubscribe("server", clientID)
|
|
|
|
for line := range ch {
|
|
if err := conn.WriteMessage(websocket.TextMessage, []byte(line)); err != nil {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *Handler) ListLogs(c *gin.Context) {
|
|
profilesDir := h.process.ProfilesDir()
|
|
files := h.collectLogFiles(profilesDir, "")
|
|
|
|
logsDir := filepath.Join(profilesDir, "logs")
|
|
if fi, err := os.Stat(logsDir); err == nil && fi.IsDir() {
|
|
files = append(files, h.collectLogFiles(logsDir, "logs")...)
|
|
}
|
|
|
|
slices.Sort(files)
|
|
if files == nil {
|
|
files = []string{}
|
|
}
|
|
c.JSON(http.StatusOK, files)
|
|
}
|
|
|
|
func (h *Handler) collectLogFiles(dir, prefix string) []string {
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var files []string
|
|
for _, e := range entries {
|
|
if !e.IsDir() {
|
|
ext := filepath.Ext(e.Name())
|
|
if ext == ".log" || ext == ".rpt" || e.Name() == "script.log" {
|
|
if prefix != "" {
|
|
files = append(files, prefix+"/"+e.Name())
|
|
} else {
|
|
files = append(files, e.Name())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return files
|
|
}
|
|
|
|
func (h *Handler) StreamSteamCMDLogs(c *gin.Context) {
|
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
|
if err != nil {
|
|
log.Printf("websocket upgrade error: %v", err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
clientID := conn.RemoteAddr().String()
|
|
ch := h.streamer.Subscribe("steamcmd", clientID)
|
|
defer h.streamer.Unsubscribe("steamcmd", clientID)
|
|
|
|
for line := range ch {
|
|
if err := conn.WriteMessage(websocket.TextMessage, []byte(line)); err != nil {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *Handler) StreamRPTLogs(c *gin.Context) {
|
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
|
if err != nil {
|
|
log.Printf("websocket upgrade error: %v", err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
profilesDir := h.process.ProfilesDir()
|
|
var currentPath string
|
|
var currentOffset int64
|
|
ticker := time.NewTicker(250 * time.Millisecond)
|
|
defer ticker.Stop()
|
|
|
|
for range ticker.C {
|
|
path := findLatestRPT(profilesDir)
|
|
|
|
if path != currentPath {
|
|
currentPath = path
|
|
currentOffset = 0
|
|
if currentPath == "" {
|
|
continue
|
|
}
|
|
conn.WriteMessage(websocket.TextMessage, []byte("--- tailing: "+filepath.Base(currentPath)+" ---"))
|
|
}
|
|
if currentPath == "" {
|
|
continue
|
|
}
|
|
|
|
fi, err := os.Stat(currentPath)
|
|
if err != nil {
|
|
currentPath = ""
|
|
continue
|
|
}
|
|
if fi.Size() <= currentOffset {
|
|
continue
|
|
}
|
|
|
|
f, err := os.Open(currentPath)
|
|
if err != nil {
|
|
currentPath = ""
|
|
continue
|
|
}
|
|
f.Seek(currentOffset, io.SeekStart)
|
|
buf := make([]byte, fi.Size()-currentOffset)
|
|
n, _ := io.ReadFull(f, buf)
|
|
f.Close()
|
|
|
|
currentOffset += int64(n)
|
|
for _, line := range strings.Split(string(buf[:n]), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
if err := conn.WriteMessage(websocket.TextMessage, []byte(line)); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func findLatestRPT(dir string) string {
|
|
pattern := filepath.Join(dir, "arma3server_x64_*.rpt")
|
|
matches, err := filepath.Glob(pattern)
|
|
if err != nil || len(matches) == 0 {
|
|
return ""
|
|
}
|
|
sort.Slice(matches, func(i, j int) bool {
|
|
fi, _ := os.Stat(matches[i])
|
|
fj, _ := os.Stat(matches[j])
|
|
return fi.ModTime().After(fj.ModTime())
|
|
})
|
|
return matches[0]
|
|
}
|
|
|
|
func (h *Handler) GetLog(c *gin.Context) {
|
|
filename := filepath.Base(c.Param("file"))
|
|
if filename == "" || strings.ContainsRune(c.Param("file"), os.PathSeparator) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid filename"})
|
|
return
|
|
}
|
|
profilesDir := h.process.ProfilesDir()
|
|
path := filepath.Join(profilesDir, filename)
|
|
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
logsPath := filepath.Join(profilesDir, "logs", filename)
|
|
if _, err2 := os.Stat(logsPath); err2 == nil {
|
|
path = logsPath
|
|
} else {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
|
|
return
|
|
}
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.String(http.StatusOK, string(data))
|
|
}
|