feat: improve log viewer with auto-scroll and security enhancements

- Add auto-scroll toggle to log panels
- Enhance SSE connection handling with reconnection logic
- Improve HTML escaping in log lines for security
- Update build script to properly handle frontend artifacts
- Add graceful shutdown to backend server
- Update authentication to support configurable username
This commit is contained in:
Luiz Costa
2025-11-18 09:59:36 -03:00
parent 5c0094d74e
commit f8360acab3
8 changed files with 134 additions and 31 deletions
+1
View File
@@ -0,0 +1 @@
Placeholder for embed
+22 -3
View File
@@ -10,8 +10,10 @@ import (
"log"
"net/http"
"os"
"os/signal"
"regexp"
"sync"
"syscall"
"github.com/hpcloud/tail"
)
@@ -23,12 +25,14 @@ var staticFiles embed.FS
var (
port string
password string
username string
)
// init() é executado antes de main(). É o lugar ideal para configurar flags.
func init() {
flag.StringVar(&port, "port", ":8080", "Porta para o servidor web (ex: :8080, 9090)")
flag.StringVar(&password, "password", "", "Senha para autenticar o acesso à interface web")
flag.StringVar(&username, "username", "admin", "Nome de usuário para autenticar o acesso à interface web")
}
// LogEntry representa uma linha de log
@@ -88,7 +92,9 @@ func handleSSE(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:8080")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
clientChan := broker.Subscribe()
defer broker.Unsubscribe(clientChan)
@@ -96,7 +102,11 @@ func handleSSE(w http.ResponseWriter, r *http.Request) {
for {
select {
case entry := <-clientChan:
data, _ := json.Marshal(entry)
data, err := json.Marshal(entry)
if err != nil {
log.Printf("Erro ao serializar entrada de log: %v", err)
continue
}
fmt.Fprintf(w, "data: %s\n\n", data)
flusher.Flush()
case <-r.Context().Done():
@@ -155,7 +165,7 @@ func authMiddleware(next http.Handler) http.Handler {
requestAuth(w)
return
}
usernameMatch := subtle.ConstantTimeCompare([]byte(user), []byte("admin")) == 1
usernameMatch := subtle.ConstantTimeCompare([]byte(user), []byte(username)) == 1
passwordMatch := subtle.ConstantTimeCompare([]byte(pass), []byte(password)) == 1
if usernameMatch && passwordMatch {
next.ServeHTTP(w, r)
@@ -182,6 +192,15 @@ func main() {
os.Exit(1)
}
// Configurar tratamento de sinais para desligamento gracioso
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigChan
log.Printf("Recebido sinal %v, encerrando servidor...", sig)
os.Exit(0)
}()
startTailing(logFiles)
distFS, err := fs.Sub(staticFiles, "dist")