|
1 | 1 | package main |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
| 5 | + "encoding/json" |
| 6 | + "fmt" |
4 | 7 | "io" |
5 | 8 | "log" |
6 | 9 | "net/http" |
7 | 10 | "os" |
8 | | - "bytes" |
| 11 | + "time" |
9 | 12 | ) |
10 | 13 |
|
11 | | -// Estruturas opcionais (mantidas para clareza) |
12 | | -type OracleRequest struct { |
13 | | - Event string `json:"event"` |
14 | | - Payload string `json:"payload"` |
15 | | - Device string `json:"device"` |
| 14 | +// ======== MODELO GENÉRICO DE RESPOSTA ======== |
| 15 | +type Response struct { |
| 16 | + Status string `json:"status"` |
| 17 | + Timestamp string `json:"timestamp"` |
| 18 | + Data interface{} `json:"data"` |
16 | 19 | } |
17 | 20 |
|
18 | | -type OracleResponse struct { |
19 | | - Hash string `json:"hash"` |
20 | | - Timestamp string `json:"timestamp"` |
21 | | - LedgerLine string `json:"ledger_line"` |
22 | | - Proof string `json:"proof"` |
23 | | - Status string `json:"status"` |
| 21 | +// ======== MODELO UNIVERSAL PARA PROVAS ======== |
| 22 | +type OraclePayload struct { |
| 23 | + Event string `json:"event"` |
| 24 | + Payload interface{} `json:"payload"` |
| 25 | + Device string `json:"device"` |
24 | 26 | } |
25 | 27 |
|
| 28 | +// ========== CONFIGURAÇÃO ========== |
| 29 | +var externalURL = os.Getenv("PRIVATE_ORACLE_URL") // seu prover interno |
| 30 | +var mqttBroker = os.Getenv("MQTT_BROKER") |
| 31 | +var chainRPC = os.Getenv("BLOCKCHAIN_RPC") |
| 32 | +var ipfsURL = os.Getenv("IPFS_URL") |
| 33 | + |
| 34 | +// =============================================================== |
| 35 | +// 🔥 INICIO DO SERVIDOR |
| 36 | +// =============================================================== |
26 | 37 | func main() { |
27 | | - // URL do backend privado (NÃO aparece no GitHub) |
28 | | - backendURL := os.Getenv("PRIVATE_ORACLE_URL") |
29 | | - if backendURL == "" { |
30 | | - log.Fatal("Erro: a variável PRIVATE_ORACLE_URL não está definida") |
| 38 | + |
| 39 | + if externalURL == "" { |
| 40 | + log.Println("⚠️ PRIVATE_ORACLE_URL não definido. Usando modo local.") |
31 | 41 | } |
32 | 42 |
|
33 | 43 | mux := http.NewServeMux() |
34 | 44 |
|
35 | | - // Endpoint chamado pela UI → repassa para o backend /mel |
36 | | - mux.HandleFunc("/oracle", func(w http.ResponseWriter, r *http.Request) { |
37 | | - if r.Method != http.MethodPost { |
38 | | - http.Error(w, "Método inválido", http.StatusMethodNotAllowed) |
39 | | - return |
40 | | - } |
| 45 | + // === ENDPOINT UNIVERSAL (UI → Backend → ZK-Prover) === |
| 46 | + mux.HandleFunc("/oracle", handleOracle) |
41 | 47 |
|
42 | | - // Ler JSON vindo da UI |
43 | | - body, err := io.ReadAll(r.Body) |
44 | | - if err != nil { |
45 | | - http.Error(w, "Erro ao ler body", http.StatusBadRequest) |
46 | | - return |
47 | | - } |
| 48 | + // === IOT === |
| 49 | + mux.HandleFunc("/iot/wifi", handleIOTWifi) |
| 50 | + mux.HandleFunc("/iot/ble", handleIOTBle) |
| 51 | + mux.HandleFunc("/iot/serial", handleIOTSerial) |
| 52 | + mux.HandleFunc("/iot/mqtt", handleIOTMQTT) |
| 53 | + mux.HandleFunc("/iot/lora", handleIOTLora) |
| 54 | + mux.HandleFunc("/iot/gps", handleGPS) |
| 55 | + mux.HandleFunc("/iot/zigbee", handleZigbee) |
48 | 56 |
|
49 | | - // Encaminhar para o backend PRIVADO → /mel |
50 | | - resp, err := http.Post( |
51 | | - backendURL+"/mel", |
52 | | - "application/json", |
53 | | - bytes.NewReader(body), |
54 | | - ) |
55 | | - if err != nil { |
56 | | - http.Error(w, "Erro ao contactar backend privado", http.StatusBadGateway) |
57 | | - return |
58 | | - } |
59 | | - defer resp.Body.Close() |
| 57 | + // === DADOS EXTERNOS === |
| 58 | + mux.HandleFunc("/price", handlePrice) |
| 59 | + mux.HandleFunc("/weather", handleWeather) |
| 60 | + mux.HandleFunc("/chain", handleChainRPC) |
| 61 | + mux.HandleFunc("/ipfs/upload", handleIPFSUpload) |
| 62 | + mux.HandleFunc("/ipfs/cat", handleIPFSCat) |
60 | 63 |
|
61 | | - // Ler resposta do backend |
62 | | - privateResp, err := io.ReadAll(resp.Body) |
63 | | - if err != nil { |
64 | | - http.Error(w, "Erro ao ler resposta backend privado", http.StatusBadGateway) |
65 | | - return |
66 | | - } |
| 64 | + // === OUTROS === |
| 65 | + mux.HandleFunc("/manual", handleManualInput) |
| 66 | + mux.HandleFunc("/test", handleTest) |
| 67 | + mux.HandleFunc("/health", handleHealth) |
| 68 | + |
| 69 | + log.Println("🔥 ORÁCULO TERRA DOURADA — BACKEND ATIVO EM :8080") |
| 70 | + http.ListenAndServe(":8080", allowCORS(mux)) |
| 71 | +} |
| 72 | + |
| 73 | +// =============================================================== |
| 74 | +// 🔥 UNIVERSAL — recebe o pacote leve da UI e envia ao backend privado |
| 75 | +// =============================================================== |
| 76 | +func handleOracle(w http.ResponseWriter, r *http.Request) { |
| 77 | + if r.Method != "POST" { |
| 78 | + http.Error(w, "Método inválido", 405) |
| 79 | + return |
| 80 | + } |
| 81 | + |
| 82 | + body, _ := io.ReadAll(r.Body) |
| 83 | + |
| 84 | + if externalURL == "" { |
| 85 | + // modo local (não envia para lugar nenhum) |
| 86 | + respond(w, "ok-local", string(body)) |
| 87 | + return |
| 88 | + } |
| 89 | + |
| 90 | + resp, err := http.Post(externalURL+"/mel", "application/json", bytes.NewReader(body)) |
| 91 | + if err != nil { |
| 92 | + http.Error(w, "Erro ao enviar ao provedor privado", 502) |
| 93 | + return |
| 94 | + } |
| 95 | + |
| 96 | + defer resp.Body.Close() |
| 97 | + dados, _ := io.ReadAll(resp.Body) |
| 98 | + |
| 99 | + w.Header().Set("Content-Type", "application/json") |
| 100 | + w.Write(dados) |
| 101 | +} |
| 102 | + |
| 103 | +// =============================================================== |
| 104 | +// 🔥 IOT — WiFi HTTP POST (sensor manda direto) |
| 105 | +// =============================================================== |
| 106 | +func handleIOTWifi(w http.ResponseWriter, r *http.Request) { |
| 107 | + var payload map[string]interface{} |
| 108 | + json.NewDecoder(r.Body).Decode(&payload) |
| 109 | + |
| 110 | + respond(w, "iot-wifi", payload) |
| 111 | +} |
| 112 | + |
| 113 | +// =============================================================== |
| 114 | +// 🔥 IOT — BLE (site lê, backend só recebe json) |
| 115 | +// =============================================================== |
| 116 | +func handleIOTBle(w http.ResponseWriter, r *http.Request) { |
| 117 | + var data map[string]interface{} |
| 118 | + json.NewDecoder(r.Body).Decode(&data) |
| 119 | + |
| 120 | + respond(w, "iot-ble", data) |
| 121 | +} |
| 122 | + |
| 123 | +// =============================================================== |
| 124 | +// 🔥 IOT — SERIAL / USB |
| 125 | +// =============================================================== |
| 126 | +func handleIOTSerial(w http.ResponseWriter, r *http.Request) { |
| 127 | + var data map[string]interface{} |
| 128 | + json.NewDecoder(r.Body).Decode(&data) |
| 129 | + |
| 130 | + respond(w, "iot-serial", data) |
| 131 | +} |
| 132 | + |
| 133 | +// =============================================================== |
| 134 | +// 🔥 IOT — MQTT |
| 135 | +// =============================================================== |
| 136 | +func handleIOTMQTT(w http.ResponseWriter, r *http.Request) { |
| 137 | + var incoming map[string]interface{} |
| 138 | + json.NewDecoder(r.Body).Decode(&incoming) |
| 139 | + |
| 140 | + incoming["broker"] = mqttBroker |
| 141 | + |
| 142 | + respond(w, "iot-mqtt", incoming) |
| 143 | +} |
| 144 | + |
| 145 | +// =============================================================== |
| 146 | +// 🔥 IOT — LoRa / LoRaWAN |
| 147 | +// =============================================================== |
| 148 | +func handleIOTLora(w http.ResponseWriter, r *http.Request) { |
| 149 | + var incoming map[string]interface{} |
| 150 | + json.NewDecoder(r.Body).Decode(&incoming) |
67 | 151 |
|
68 | | - // Retornar para a UI |
69 | | - w.Header().Set("Content-Type", "application/json") |
70 | | - w.Write(privateResp) |
| 152 | + incoming["lora_gateway"] = "LOCAL-GATEWAY" |
| 153 | + |
| 154 | + respond(w, "iot-lora", incoming) |
| 155 | +} |
| 156 | + |
| 157 | +// =============================================================== |
| 158 | +// 🔥 IOT — GPS |
| 159 | +// =============================================================== |
| 160 | +func handleGPS(w http.ResponseWriter, r *http.Request) { |
| 161 | + var gps map[string]interface{} |
| 162 | + json.NewDecoder(r.Body).Decode(&gps) |
| 163 | + |
| 164 | + respond(w, "iot-gps", gps) |
| 165 | +} |
| 166 | + |
| 167 | +// =============================================================== |
| 168 | +// 🔥 IOT — Zigbee |
| 169 | +// =============================================================== |
| 170 | +func handleZigbee(w http.ResponseWriter, r *http.Request) { |
| 171 | + var data map[string]interface{} |
| 172 | + json.NewDecoder(r.Body).Decode(&data) |
| 173 | + |
| 174 | + respond(w, "iot-zigbee", data) |
| 175 | +} |
| 176 | + |
| 177 | +// =============================================================== |
| 178 | +// 🔥 PRICE FETCHER (cripto, dólar, commodity, etc.) |
| 179 | +// =============================================================== |
| 180 | +func handlePrice(w http.ResponseWriter, r *http.Request) { |
| 181 | + moeda := r.URL.Query().Get("symbol") |
| 182 | + if moeda == "" { |
| 183 | + moeda = "BTC" |
| 184 | + } |
| 185 | + |
| 186 | + api := fmt.Sprintf("https://api.binance.com/api/v3/ticker/price?symbol=%sUSDT", moeda) |
| 187 | + resp, _ := http.Get(api) |
| 188 | + defer resp.Body.Close() |
| 189 | + |
| 190 | + corpo, _ := io.ReadAll(resp.Body) |
| 191 | + |
| 192 | + w.Header().Set("Content-Type", "application/json") |
| 193 | + w.Write(corpo) |
| 194 | +} |
| 195 | + |
| 196 | +// =============================================================== |
| 197 | +// 🔥 WEATHER (previsão do tempo) |
| 198 | +// =============================================================== |
| 199 | +func handleWeather(w http.ResponseWriter, r *http.Request) { |
| 200 | + cidade := r.URL.Query().Get("city") |
| 201 | + if cidade == "" { |
| 202 | + cidade = "São Paulo" |
| 203 | + } |
| 204 | + |
| 205 | + respond(w, "weather", map[string]string{ |
| 206 | + "cidade": cidade, |
| 207 | + "status": "sol", |
| 208 | + "temp": "28", |
71 | 209 | }) |
| 210 | +} |
| 211 | + |
| 212 | +// =============================================================== |
| 213 | +// 🔥 BLOCKCHAIN RPC |
| 214 | +// =============================================================== |
| 215 | +func handleChainRPC(w http.ResponseWriter, r *http.Request) { |
| 216 | + var req interface{} |
| 217 | + json.NewDecoder(r.Body).Decode(&req) |
72 | 218 |
|
73 | | - // CORS |
74 | | - handler := corsMiddleware(mux) |
| 219 | + b, _ := json.Marshal(req) |
| 220 | + resp, _ := http.Post(chainRPC, "application/json", bytes.NewReader(b)) |
| 221 | + defer resp.Body.Close() |
75 | 222 |
|
76 | | - log.Println("🌐 Gateway Go rodando em http://localhost:7070") |
77 | | - http.ListenAndServe(":7070", handler) |
| 223 | + dados, _ := io.ReadAll(resp.Body) |
| 224 | + w.Write(dados) |
78 | 225 | } |
79 | 226 |
|
80 | | -// Middleware simples de CORS |
81 | | -func corsMiddleware(next http.Handler) http.Handler { |
| 227 | +// =============================================================== |
| 228 | +// 🔥 IPFS UPLOAD |
| 229 | +// =============================================================== |
| 230 | +func handleIPFSUpload(w http.ResponseWriter, r *http.Request) { |
| 231 | + var dado map[string]interface{} |
| 232 | + json.NewDecoder(r.Body).Decode(&dado) |
| 233 | + |
| 234 | + respond(w, "ipfs-upload", dado) |
| 235 | +} |
| 236 | + |
| 237 | +// =============================================================== |
| 238 | +// 🔥 IPFS CAT |
| 239 | +// =============================================================== |
| 240 | +func handleIPFSCat(w http.ResponseWriter, r *http.Request) { |
| 241 | + cid := r.URL.Query().Get("cid") |
| 242 | + respond(w, "ipfs-cat", map[string]string{"cid": cid}) |
| 243 | +} |
| 244 | + |
| 245 | +// =============================================================== |
| 246 | +// 🔥 MANUAL INPUT |
| 247 | +// =============================================================== |
| 248 | +func handleManualInput(w http.ResponseWriter, r *http.Request) { |
| 249 | + var dado map[string]interface{} |
| 250 | + json.NewDecoder(r.Body).Decode(&dado) |
| 251 | + |
| 252 | + respond(w, "manual", dado) |
| 253 | +} |
| 254 | + |
| 255 | +// =============================================================== |
| 256 | +// 🔥 MISC |
| 257 | +// =============================================================== |
| 258 | +func handleTest(w http.ResponseWriter, r *http.Request) { |
| 259 | + respond(w, "test", "ok") |
| 260 | +} |
| 261 | + |
| 262 | +func handleHealth(w http.ResponseWriter, r *http.Request) { |
| 263 | + respond(w, "health", "alive") |
| 264 | +} |
| 265 | + |
| 266 | +// =============================================================== |
| 267 | +// 🔧 RESPOSTA UNIVERSAL |
| 268 | +// =============================================================== |
| 269 | +func respond(w http.ResponseWriter, status string, data interface{}) { |
| 270 | + resposta := Response{ |
| 271 | + Status: status, |
| 272 | + Timestamp: time.Now().Format(time.RFC3339), |
| 273 | + Data: data, |
| 274 | + } |
| 275 | + |
| 276 | + w.Header().Set("Content-Type", "application/json") |
| 277 | + json.NewEncoder(w).Encode(resposta) |
| 278 | +} |
| 279 | + |
| 280 | +// =============================================================== |
| 281 | +// 🔧 CORS |
| 282 | +// =============================================================== |
| 283 | +func allowCORS(next http.Handler) http.Handler { |
82 | 284 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
83 | 285 | w.Header().Set("Access-Control-Allow-Origin", "*") |
| 286 | + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS") |
84 | 287 | w.Header().Set("Access-Control-Allow-Headers", "Content-Type") |
85 | | - w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") |
86 | | - |
87 | | - if r.Method == http.MethodOptions { |
| 288 | + if r.Method == "OPTIONS" { |
88 | 289 | return |
89 | 290 | } |
90 | | - |
91 | 291 | next.ServeHTTP(w, r) |
92 | 292 | }) |
93 | 293 | } |
94 | 294 |
|
| 295 | + |
0 commit comments