diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a3a4bc29..c65b0f6a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,7 +11,11 @@ "NODE_VERSION": "lts/*" } }, - "runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], + "runArgs": [ + "--cap-add=SYS_PTRACE", + "--security-opt", + "seccomp=unconfined" + ], "customizations": { "vscode": { // Set *default* container specific settings.json values on container create. @@ -38,10 +42,8 @@ }, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "go version", - // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode" -} +} \ No newline at end of file diff --git a/internal/api/backresthandler.go b/internal/api/backresthandler.go index db4fa653..9f2243a1 100644 --- a/internal/api/backresthandler.go +++ b/internal/api/backresthandler.go @@ -541,6 +541,7 @@ func (s *BackrestHandler) GetLogs(ctx context.Context, req *connect.Request[v1.L } return fmt.Errorf("get log data %v: %w", req.Msg.GetRef(), err) } + defer s.logStore.Unsubscribe(req.Msg.Ref, ch) doneCh := make(chan struct{}) @@ -550,20 +551,12 @@ func (s *BackrestHandler) GetLogs(ctx context.Context, req *connect.Request[v1.L defer interval.Stop() go func() { - for { - select { - case data, ok := <-ch: - if !ok { - close(doneCh) - return - } - mu.Lock() - buf.Write(data) - mu.Unlock() - case <-ctx.Done(): - return - } + for data := range ch { + mu.Lock() + buf.Write(data) + mu.Unlock() } + close(doneCh) }() flushHelper := func() error { diff --git a/internal/logwriter/livelog.go b/internal/logwriter/livelog.go index 03444976..244fbdfb 100644 --- a/internal/logwriter/livelog.go +++ b/internal/logwriter/livelog.go @@ -70,9 +70,14 @@ func (t *LiveLog) Unsubscribe(id string, ch chan []byte) { if w, ok := t.writers[id]; ok { w.mu.Lock() defer w.mu.Unlock() - w.subscribers = slices.DeleteFunc(w.subscribers, func(c chan []byte) bool { + + idx := slices.IndexFunc(w.subscribers, func(c chan []byte) bool { return c == ch }) + if idx >= 0 { + close(ch) + w.subscribers = append(w.subscribers[:idx], w.subscribers[idx+1:]...) + } } } @@ -194,6 +199,7 @@ func (t *LiveLogWriter) Close() error { for _, ch := range t.subscribers { close(ch) } + t.subscribers = nil return t.fh.Close() } diff --git a/webui/src/components/LogView.tsx b/webui/src/components/LogView.tsx index f0fcb1fe..6d0205aa 100644 --- a/webui/src/components/LogView.tsx +++ b/webui/src/components/LogView.tsx @@ -57,7 +57,10 @@ export const LogView = ({ logref }: { logref: string }) => { }} > {lines.map((line, i) => ( -
+{line}))} diff --git a/webui/src/components/OperationRow.tsx b/webui/src/components/OperationRow.tsx index 616ea85d..301e8c03 100644 --- a/webui/src/components/OperationRow.tsx +++ b/webui/src/components/OperationRow.tsx @@ -212,6 +212,7 @@ export const OperationRow = ({ }); } else if (operation.op.case === "operationPrune") { const prune = operation.op.value; + expandedBodyItems.push("prune"); bodyItems.push({ key: "prune", label: "Prune Output", @@ -223,6 +224,7 @@ export const OperationRow = ({ }); } else if (operation.op.case === "operationCheck") { const check = operation.op.value; + expandedBodyItems.push("check"); bodyItems.push({ key: "check", label: "Check Output", @@ -242,6 +244,9 @@ export const OperationRow = ({ } else if (operation.op.case === "operationRunHook") { const hook = operation.op.value; if (operation.logref) { + if (operation.status === OperationStatus.STATUS_INPROGRESS) { + expandedBodyItems.push("logref"); + } bodyItems.push({ key: "logref", label: "Hook Output",