Skip to content

feat(api): add Raspberry reboot endpoint and logs UI action#156

Open
pacioc193 wants to merge 2 commits intowimaha:mainfrom
pacioc193:feature/reboot-api-raspberry
Open

feat(api): add Raspberry reboot endpoint and logs UI action#156
pacioc193 wants to merge 2 commits intowimaha:mainfrom
pacioc193:feature/reboot-api-raspberry

Conversation

@pacioc193
Copy link

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

  • Add new route:
    • POST /api/admin/reboot
  • Add backend handler:
    • RebootSystem in internal/api/handlers/logs.go
    • Executes reboot asynchronously after returning HTTP response
    • Tries commands in order:
      • sudo shutdown -r now
      • /sbin/shutdown -r now
      • shutdown -r now
  • Add frontend action in logs page:
    • New button Reboot Raspberry in html/logs.html
    • Double confirmation prompt
    • Calls /api/admin/reboot via fetch
    • Disables button while request is in progress and reports result

Why

Operators can now reboot the device directly from the web UI without SSH access.

Validation

  • go test ./internal/api/... passed

Notes

  • Reboot command requires appropriate OS permissions (sudoers/service user capabilities).

Copilot AI review requested due to automatic review settings March 11, 2026 22:25
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/reboot route 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.

Comment on lines +86 to +94
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)

Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +105
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."
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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".

Copilot uses AI. Check for mistakes.
Comment on lines +117 to +123
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
err := cmd.Run()
cancel()
if err == nil {
return nil
}
lastErr = err
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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))

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants