Adding a Web Dashboard to a Windows Console App
TECH INSIGHT / EXPLAINED
A local dashboard can show a console application’s state without becoming another route for accidental control. Start with one read-only JSON endpoint and a clear boundary between snapshots and commands.
Local execution of the actual project
These captures show the existing project UI or functions executed locally with non-private sample inputs. Each caption states the scope of the execution.
A snapshot must own its data
HTTP handlers run concurrently. A mutex-protected copy is often enough; an actor goroutine is useful when state transitions already have one owner. Either way, copy maps and slices before returning them. Copying only a struct leaves the underlying arrays shared. If an actor answers through a channel, use a buffered reply channel and select on request cancellation and service shutdown so abandoned clients cannot stall the actor.
Bind locally and fail closed
Bind to 127.0.0.1 by default. Binding to 0.0.0.0 listens on all IPv4 interfaces; internet reachability additionally depends on routing and firewall rules. Never infer “local only” from an empty access key. Reject remote-bind startup when required authentication is missing. Send credentials in an authorization header, not URL parameters that can appear in history and logs. Constant-time comparison is one detail; it does not replace TLS, authorization or exposure control.
Polling needs a failure contract
For example, refresh every three seconds only after the previous request completes, set a request timeout, and show both observation timestamp and connection status. On failure retain the last value with a visible stale marker. A green page with a ten-minute-old balance is misleading. Give the HTTP server read-header, write and idle timeouts appropriate to the endpoints, and shut it down with the application.
A read screen and an action screen have different risks
Use GET for state. Settings, stop and order cancellation require authenticated POST-style actions with server-side permission and input checks; browser cookie sessions also require CSRF defenses. Test that GET cannot trigger them and an absent key yields rejection. For remote viewing, prefer a protected tunnel/VPN or authenticated HTTPS gateway rather than public unauthenticated control routes.
Hiding a window is not process supervision
Console visibility APIs affect a window where supported; they do not create a tray app, service, or restart policy. Keep Windows-specific code in a _windows.go file and account for terminal-host differences. Before adding hide controls, make shutdown and health reporting reliable.
Run a minimal read-only endpoint
Save this as main.go and run go run main.go. Open http://127.0.0.1:9333/api/state to receive {"updated_at":"...Z","running":false}. This standalone demo has no trading engine or command endpoints. If updates are added, they must take the same mutex; production shutdown should use Server.Shutdown.
package main
import (
"encoding/json"
"log"
"net/http"
"sync"
"time"
)
type snapshot struct {
UpdatedAt time.Time `json:"updated_at"`
Running bool `json:"running"`
}
func readOnlyDashboard() http.Handler {
var mu sync.RWMutex
state := snapshot{UpdatedAt: time.Now().UTC(), Running: false}
mux := http.NewServeMux()
mux.HandleFunc("/api/state", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.Header().Set("Allow", "GET")
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
mu.RLock()
copy := state // Only value fields; maps/slices need deeper copies.
mu.RUnlock()
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Cache-Control", "no-store")
if err := json.NewEncoder(w).Encode(copy); err != nil { log.Print(err) }
})
return mux
}
func main() {
srv := &http.Server{
Addr: "127.0.0.1:9333", Handler: readOnlyDashboard(),
ReadHeaderTimeout: 3*time.Second, WriteTimeout: 5*time.Second,
IdleTimeout: 30*time.Second,
}
log.Fatal(srv.ListenAndServe())
}Documentation and next steps
Content and examples reviewed: 2026-09-29
댓글
댓글 쓰기