ShreeshantXD commited on
Commit
84fb786
·
1 Parent(s): 18e9b8c

Add dashboard reverse proxy at /dashboard

Browse files
Files changed (3) hide show
  1. Dockerfile +3 -2
  2. README.md +15 -0
  3. main.go +62 -3
Dockerfile CHANGED
@@ -58,8 +58,9 @@ RUN useradd -m -u 1000 user && \
58
  chown -R user:user /app && \
59
  chown -R user:user /var/run/supervisor /var/log/supervisor
60
 
61
- # 7860 = Env Server (main OpenEnv endpoint), 7861 = Dashboard
62
- EXPOSE 7860 7861
 
63
 
64
  # Run supervisor as root to manage both services (required for multi-process supervision)
65
  CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf", "-n"]
 
58
  chown -R user:user /app && \
59
  chown -R user:user /var/run/supervisor /var/log/supervisor
60
 
61
+ # EXPOSE 7860 only - this is the main OpenEnv API endpoint (reverse proxy + /dashboard)
62
+ # Port 7861 (dashboard) runs internally only and is accessed via /dashboard proxy
63
+ EXPOSE 7860
64
 
65
  # Run supervisor as root to manage both services (required for multi-process supervision)
66
  CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf", "-n"]
README.md CHANGED
@@ -21,6 +21,21 @@ license: mit
21
 
22
  ---
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  ## Overview
25
 
26
  GridMind-RL is a reinforcement learning environment for training and evaluating intelligent control policies in industrial building energy management. The environment simulates realistic HVAC control, thermal storage management, batch job scheduling, and demand response scenarios under stochastic electricity pricing and grid stress events.
 
21
 
22
  ---
23
 
24
+ ---
25
+
26
+ ## 🚀 Live Demo
27
+
28
+ | | URL |
29
+ |--|-----|
30
+ | **Environment API** | https://lo-kyu-gridmind.hf.space |
31
+ | **Live Dashboard** | https://lo-kyu-gridmind.hf.space/dashboard |
32
+
33
+ **Quick test:**
34
+ ```bash
35
+ curl https://lo-kyu-gridmind.hf.space/health
36
+ curl https://lo-kyu-gridmind.hf.space/tasks
37
+ ```
38
+
39
  ## Overview
40
 
41
  GridMind-RL is a reinforcement learning environment for training and evaluating intelligent control policies in industrial building energy management. The environment simulates realistic HVAC control, thermal storage management, batch job scheduling, and demand response scenarios under stochastic electricity pricing and grid stress events.
main.go CHANGED
@@ -8,9 +8,13 @@ import (
8
  "fmt"
9
  "log"
10
  "math"
 
11
  "net/http"
 
 
12
  "os"
13
  "strconv"
 
14
  "sync"
15
  "sync/atomic"
16
  "time"
@@ -32,8 +36,8 @@ type Metrics struct {
32
  rewardMin float64
33
  rewardMax float64
34
  // Histograms
35
- actionBuckets map[string]int64 // hvac bucket counts
36
- errorCount int64
37
  }
38
 
39
  var metrics = &Metrics{
@@ -143,6 +147,9 @@ func (s *Server) routes() *http.ServeMux {
143
  mux.HandleFunc("/grade", s.handleGrade)
144
  mux.HandleFunc("/tasks", s.handleTasks)
145
  mux.HandleFunc("/metrics", s.handleMetrics)
 
 
 
146
  return mux
147
  }
148
 
@@ -333,6 +340,58 @@ func (s *Server) handleMetrics(w http.ResponseWriter, r *http.Request) {
333
  fmt.Fprint(w, metrics.prometheus())
334
  }
335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  // ──────────────────────────────────────────────
337
  // Entry point
338
  // ──────────────────────────────────────────────
@@ -354,7 +413,7 @@ func main() {
354
  srv.envMgr.Reset(env.ResetRequest{Seed: &seed, TaskID: 1, NumBuildings: 1})
355
 
356
  log.Printf("GridMind-RL environment server starting on :%s", port)
357
- log.Printf("Endpoints: GET /health /ping /state /replay /grade /tasks /metrics | POST /reset /step")
358
 
359
  mux := withCORS(withLogging(srv.routes()))
360
  if err := http.ListenAndServe(":"+port, mux); err != nil {
 
8
  "fmt"
9
  "log"
10
  "math"
11
+ "net"
12
  "net/http"
13
+ "net/http/httputil"
14
+ "net/url"
15
  "os"
16
  "strconv"
17
+ "strings"
18
  "sync"
19
  "sync/atomic"
20
  "time"
 
36
  rewardMin float64
37
  rewardMax float64
38
  // Histograms
39
+ actionBuckets map[string]int64 // hvac bucket counts
40
+ errorCount int64
41
  }
42
 
43
  var metrics = &Metrics{
 
147
  mux.HandleFunc("/grade", s.handleGrade)
148
  mux.HandleFunc("/tasks", s.handleTasks)
149
  mux.HandleFunc("/metrics", s.handleMetrics)
150
+ // Reverse proxy for dashboard (runs on port 7861 internally)
151
+ mux.HandleFunc("/dashboard", s.handleDashboardProxy)
152
+ mux.HandleFunc("/dashboard/", s.handleDashboardProxy)
153
  return mux
154
  }
155
 
 
340
  fmt.Fprint(w, metrics.prometheus())
341
  }
342
 
343
+ // ── /dashboard (Reverse Proxy) ────────────────────────────────────────────
344
+
345
+ func (s *Server) handleDashboardProxy(w http.ResponseWriter, r *http.Request) {
346
+ // Target URL for the dashboard service (running on localhost:7861)
347
+ target, err := url.Parse("http://localhost:7861")
348
+ if err != nil {
349
+ http.Error(w, "proxy configuration error", http.StatusInternalServerError)
350
+ return
351
+ }
352
+
353
+ // Create a custom director to modify the request
354
+ director := func(req *http.Request) {
355
+ // Strip /dashboard prefix
356
+ path := req.URL.Path
357
+ if strings.HasPrefix(path, "/dashboard") {
358
+ path = strings.TrimPrefix(path, "/dashboard")
359
+ if path == "" {
360
+ path = "/"
361
+ }
362
+ }
363
+
364
+ // Set up the new request
365
+ req.URL.Scheme = target.Scheme
366
+ req.URL.Host = target.Host
367
+ if target.Path != "" {
368
+ req.URL.Path = target.Path + path
369
+ } else {
370
+ req.URL.Path = path
371
+ }
372
+ req.RequestURI = ""
373
+
374
+ // Preserve original host header for dashboard API calls
375
+ if req.Header.Get("X-Forwarded-Host") == "" {
376
+ req.Header.Set("X-Forwarded-For", getClientIP(r))
377
+ req.Header.Set("X-Forwarded-Proto", "https")
378
+ }
379
+ }
380
+
381
+ // Use ReverseProxy with custom director
382
+ proxy := &httputil.ReverseProxy{Director: director}
383
+ proxy.ServeHTTP(w, r)
384
+ }
385
+
386
+ // Helper: extract client IP from request
387
+ func getClientIP(r *http.Request) string {
388
+ ip, _, err := net.SplitHostPort(r.RemoteAddr)
389
+ if err != nil {
390
+ return r.RemoteAddr
391
+ }
392
+ return ip
393
+ }
394
+
395
  // ──────────────────────────────────────────────
396
  // Entry point
397
  // ──────────────────────────────────────────────
 
413
  srv.envMgr.Reset(env.ResetRequest{Seed: &seed, TaskID: 1, NumBuildings: 1})
414
 
415
  log.Printf("GridMind-RL environment server starting on :%s", port)
416
+ log.Printf("Endpoints: GET /health /ping /state /replay /grade /tasks /metrics /dashboard | POST /reset /step")
417
 
418
  mux := withCORS(withLogging(srv.routes()))
419
  if err := http.ListenAndServe(":"+port, mux); err != nil {