AIOTEC Finance Platform

Live Indicators (Google Sheet Stream)

Stream: WAITING
HTML ############################################ # WRITE main.go (FULL SFU) ############################################ cat > "$APP_DIR/main.go" <<'GO' package main import ( "encoding/json" "log" "net/http" "sync" "github.com/google/uuid" "github.com/pion/webrtc/v4" "finance/strategy" ) type Peer struct { id string pc *webrtc.PeerConnection } var ( peers = make(map[string]*Peer) mu sync.Mutex ) func main() { log.Println("AIOTEC NETWORK CORE START") // ????? Strategy Engine go strategy.StartStrategyEngine() // =============================== // Media Engine (EMPTY: no audio / no video) // =============================== m := &webrtc.MediaEngine{} api := webrtc.NewAPI(webrtc.WithMediaEngine(m)) // =============================== // Static files (UI) // =============================== http.Handle("/", http.FileServer(http.Dir("web"))) // =============================== // WebRTC OFFER HANDLER // =============================== http.HandleFunc("/offer", func(w http.ResponseWriter, r *http.Request) { peerID := uuid.New().String() pc, err := api.NewPeerConnection(webrtc.Configuration{ BundlePolicy: webrtc.BundlePolicyMaxBundle, RTCPMuxPolicy: webrtc.RTCPMuxPolicyRequire, ICEServers: []webrtc.ICEServer{ {URLs: []string{"stun:stun.l.google.com:19302"}}, { URLs: []string{"turn:159.198.46.164:3478"}, Username: "test", Credential: "test", }, }, }) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Println("NEW PEER:", peerID) // =============================== // ICE STATE // =============================== pc.OnICEConnectionStateChange(func(s webrtc.ICEConnectionState) { log.Println("ICE", peerID, "->", s.String()) }) // =============================== // PC STATE // =============================== pc.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { log.Println("PC", peerID, "->", s.String()) if s == webrtc.PeerConnectionStateClosed || s == webrtc.PeerConnectionStateFailed { mu.Lock() delete(peers, peerID) mu.Unlock() } }) peer := &Peer{id: peerID, pc: pc} mu.Lock() peers[peerID] = peer mu.Unlock() // =============================== // SDP EXCHANGE // =============================== var offer webrtc.SessionDescription if err := json.NewDecoder(r.Body).Decode(&offer); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if err := pc.SetRemoteDescription(offer); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } answer, err := pc.CreateAnswer(nil) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if err := pc.SetLocalDescription(answer); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } <-webrtc.GatheringCompletePromise(pc) json.NewEncoder(w).Encode(pc.LocalDescription()) }) // =============================== // SERVER // =============================== log.Println("NETWORK CORE LISTENING ON :9100") log.Fatal(http.ListenAndServe(":9100", nil)) }