Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Lerd uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Fixed

- **PHP FPM fails to start on fresh installs** — the shared hosts file (`~/.local/share/lerd/hosts`) is bind-mounted into every PHP-FPM container. If no site had ever been linked, the file did not exist and podman refused to start the container with `statfs: no such file or directory`. `WriteFPMQuadlet` now ensures the file is created before the container is started.
- **Queue log streaming was a stale duplicate of the shared implementation** — the `/api/queue/<site>/logs` SSE handler had its own inline copy of the log streaming logic instead of calling the shared `streamUnitLogs` helper used by every other worker (horizon, schedule, reverb, stripe). The duplicate is removed.

---

Expand Down
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Lerd uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Fixed

- **PHP FPM fails to start on fresh installs** — the shared hosts file (`~/.local/share/lerd/hosts`) is bind-mounted into every PHP-FPM container. If no site had ever been linked, the file did not exist and podman refused to start the container with `statfs: no such file or directory`. `WriteFPMQuadlet` now ensures the file is created before the container is started.
- **Queue log streaming was a stale duplicate of the shared implementation** — the `/api/queue/<site>/logs` SSE handler had its own inline copy of the log streaming logic instead of calling the shared `streamUnitLogs` helper used by every other worker (horizon, schedule, reverb, stripe). The duplicate is removed.

---

Expand Down
43 changes: 1 addition & 42 deletions internal/ui/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1577,48 +1577,7 @@ func handleQueueLogs(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
return
}
unit := "lerd-queue-" + parts[0]

flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "streaming not supported", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("X-Accel-Buffering", "no")

pr, pw := io.Pipe()
cmd := exec.CommandContext(r.Context(), "journalctl", "--user", "-u", unit, "-f", "--no-pager", "-n", "100", "--output=cat")
cmd.Stdout = pw
cmd.Stderr = pw

if err := cmd.Start(); err != nil {
fmt.Fprintf(w, "data: error starting logs: %s\n\n", err.Error())
flusher.Flush()
return
}

go func() {
cmd.Wait() //nolint:errcheck
pw.Close()
}()

scanner := bufio.NewScanner(pr)
for scanner.Scan() {
line := scanner.Text()
escaped := strings.ReplaceAll(line, "\\", "\\\\")
fmt.Fprintf(w, "data: %s\n\n", escaped)
flusher.Flush()
if r.Context().Err() != nil {
break
}
}
if cmd.Process != nil {
cmd.Process.Kill() //nolint:errcheck
}
streamUnitLogs(w, r, "lerd-queue-"+parts[0])
}

// SettingsResponse is the response for GET /api/settings.
Expand Down
Loading