feat(api): add Raspberry reboot endpoint and logs UI action#156
feat(api): add Raspberry reboot endpoint and logs UI action#156pacioc193 wants to merge 2 commits intowimaha:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an admin reboot capability to the TeslaBleHttpProxy web app by introducing a backend endpoint that triggers a Raspberry Pi reboot and wiring it to a new “Reboot Raspberry” action on the logs UI.
Changes:
- Add
POST /api/admin/rebootroute and handler to initiate a system reboot asynchronously. - Implement reboot command execution with fallback command list and timeouts.
- Add a logs page button with double-confirmation that calls the reboot endpoint and displays the result.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| internal/api/routes/routes.go | Registers the new admin reboot API route. |
| internal/api/handlers/logs.go | Implements the reboot handler and command execution logic. |
| html/logs.html | Adds a “Reboot Raspberry” button and client-side fetch flow to trigger reboot. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| func RebootSystem(w http.ResponseWriter, r *http.Request) { | ||
| var response models.Response | ||
| response.Command = "system_reboot" | ||
| response.Result = false | ||
|
|
||
| defer commonDefer(w, &response) | ||
|
|
||
| logging.Warn("System reboot requested", "remote_addr", r.RemoteAddr) | ||
|
|
There was a problem hiding this comment.
POST /api/admin/reboot triggers a host reboot but currently has no access control (authn/authz) or CSRF mitigation. As-is, any party that can reach the HTTP server (or a malicious webpage via CSRF) can reboot the device. Please add a protection mechanism (e.g., require an admin token/header, restrict to localhost/private subnets, and/or validate Origin/Referer for browser-based calls) before executing the reboot.
| func RebootSystem(w http.ResponseWriter, r *http.Request) { | ||
| var response models.Response | ||
| response.Command = "system_reboot" | ||
| response.Result = false | ||
|
|
||
| defer commonDefer(w, &response) | ||
|
|
||
| logging.Warn("System reboot requested", "remote_addr", r.RemoteAddr) | ||
|
|
||
| go func() { | ||
| // Give the HTTP response time to flush before rebooting the host. | ||
| time.Sleep(750 * time.Millisecond) | ||
| if err := executeReboot(); err != nil { | ||
| logging.Error("Failed to execute reboot command", "error", err) | ||
| } | ||
| }() | ||
|
|
||
| response.Result = true | ||
| response.Reason = "System reboot initiated. The device will restart shortly." | ||
| } |
There was a problem hiding this comment.
RebootSystem sets response.Result = true unconditionally and returns success even if all reboot commands fail (failure is only logged from the goroutine). This will mislead the UI/operator when permissions or binaries are missing. Consider doing a quick preflight before responding (e.g., exec.LookPath / sudo -n checks) and returning a failure status if reboot cannot be initiated, or adjust the API semantics to explicitly indicate "request accepted" (e.g., 202) rather than "reboot succeeded".
| cmd := exec.CommandContext(ctx, args[0], args[1:]...) | ||
| err := cmd.Run() | ||
| cancel() | ||
| if err == nil { | ||
| return nil | ||
| } | ||
| lastErr = err |
There was a problem hiding this comment.
executeReboot discards stdout/stderr from the shutdown commands, making it difficult to diagnose common failures (e.g., sudo password required, permission denied, binary missing). Capture and log the combined output (or include it in the returned error), and consider using defer cancel() right after creating the context to ensure cleanup on all paths.
| cmd := exec.CommandContext(ctx, args[0], args[1:]...) | |
| err := cmd.Run() | |
| cancel() | |
| if err == nil { | |
| return nil | |
| } | |
| lastErr = err | |
| defer cancel() | |
| cmd := exec.CommandContext(ctx, args[0], args[1:]...) | |
| output, err := cmd.CombinedOutput() | |
| if err == nil { | |
| return nil | |
| } | |
| lastErr = fmt.Errorf("command %v failed: %w (output: %s)", args, err, string(output)) |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
This PR adds a dedicated admin API endpoint to reboot the Raspberry Pi and integrates a reboot action in the logs page UI.
Changes
POST /api/admin/rebootRebootSystemininternal/api/handlers/logs.gosudo shutdown -r now/sbin/shutdown -r nowshutdown -r nowReboot Raspberryinhtml/logs.html/api/admin/rebootviafetchWhy
Operators can now reboot the device directly from the web UI without SSH access.
Validation
go test ./internal/api/...passedNotes
sudoers/service user capabilities).