platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
82
selective-vpn-api/app/trace_tail.go
Normal file
82
selective-vpn-api/app/trace_tail.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// tail helper
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
const defaultTailMaxBytes = 512 * 1024
|
||||
|
||||
func tailFile(path string, maxLines int) []string {
|
||||
if maxLines <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// читаем лимит из env, если задан
|
||||
maxBytes := defaultTailMaxBytes
|
||||
if env := os.Getenv("SVPN_TAIL_MAX_BYTES"); env != "" {
|
||||
if n, err := strconv.Atoi(env); err == nil && n > 0 {
|
||||
maxBytes = n
|
||||
}
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
// файла нет или нет прав — просто ничего не отдаём
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
size := fi.Size()
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// с какого смещения читаем хвост
|
||||
start := int64(0)
|
||||
if size > int64(maxBytes) {
|
||||
start = size - int64(maxBytes)
|
||||
}
|
||||
|
||||
// двигаем указатель в файле
|
||||
if _, err := f.Seek(start, io.SeekStart); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// читаем хвост
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// режем по строкам
|
||||
lines := strings.Split(string(data), "\n")
|
||||
|
||||
// если мы начали читать с середины файла (start > 0),
|
||||
// первая строка почти наверняка обрезана — выбрасываем её.
|
||||
if start > 0 && len(lines) > 0 {
|
||||
lines = lines[1:]
|
||||
}
|
||||
|
||||
// убираем финальную пустую строку, если есть
|
||||
if n := len(lines); n > 0 && lines[n-1] == "" {
|
||||
lines = lines[:n-1]
|
||||
}
|
||||
|
||||
// берём только последние maxLines
|
||||
if len(lines) > maxLines {
|
||||
lines = lines[len(lines)-maxLines:]
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
Reference in New Issue
Block a user