code wiki / (root) / nx_iot_hub_serve.nx

nx_iot_hub_serve.nx source

↩ module page · 118 lines · 5032 B

1// nx_iot_hub_serve.nx -- the sovereign IoT HUB HTTP server (killer-feature rung 3). 2// 3// Operator vision: manage the house's IoT entirely from the FIRST-PARTY NISHI BROWSER, LAN-only, 4// available even with the internet out. This is the always-on hub surface: it renders the zero-JS 5// dashboard (nx_iot_dashboard) on GET / and accepts the control POSTs on /home/cmd -- all over the 6// LAN, no cloud. Composes the proven nx_http_server (bind/listen/accept + parse + respond) so there 7// is NO new protocol code. Like nx_vizsla_serve: a bare run prints usage; `serve <port>` runs the 8// accept loop; `handle <reqfile>` processes one captured request (GATEABLE without sockets). 9// 10// Rung 2 will wire /home/cmd to the Nishi Tuya-LAN client (TCP/6668 + AES + local_key). For now the 11// command is parsed + acknowledged (303-style 200) so the full browser->hub loop is provable today. 12// license_tier: ORIGINAL 13import "nx_http_server.nx" 14import "nx_iot_dashboard.nx" 15const HUB_MAGIC_18099: i64 = 18099 16 17const HUB_REQ_CAP: i64 = 8192 18const HUB_PAGE_CAP: i64 = 262144 19 20func hub_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 21 22// bytes-equal check of req_buf[off..off+len] vs literal 23func hub_match(req: *u8, off: i64, len: i64, lit: *u8) -> i64 { 24 var i: i64 = 0 25 while lit[i] != (0 as u8) { 26 if i >= len { return 0 } 27 if (req[off + i] as i64) != (lit[i] as i64) { return 0 } 28 i = i + 1 29 } 30 return 1 31} 32 33// content-type literals 34func hub_ct_html(box: *u8) -> i64 { 35 let s: *u8 = "text/html; charset=utf-8" as *u8 36 var i: i64 = 0 37 while s[i] != (0 as u8) { box[i] = s[i]; i = i + 1 } 38 return i 39} 40 41// Serve one already-accepted client fd: read request, route, respond. Returns NXS_*. 42func hub_serve_client(cfd: i64, page: *u8, page_n: i64) -> i64 { 43 let req: *u8 = sys_mmap(HUB_REQ_CAP) 44 let m: *i64 = sys_mmap(8) as *i64 45 let poff: *i64 = sys_mmap(8) as *i64 46 let plen: *i64 = sys_mmap(8) as *i64 47 let clen: *i64 = sys_mmap(8) as *i64 48 let boff: *i64 = sys_mmap(8) as *i64 49 let rn: *i64 = sys_mmap(8) as *i64 50 let rc: i64 = nx_http_server_read_request(cfd, req, HUB_REQ_CAP, m, poff, plen, clen, boff, rn) 51 if rc != NXS_OK { sys_close(cfd); return rc } 52 53 let ct: *u8 = sys_mmap(64) 54 let ctn: i64 = hub_ct_html(ct) 55 56 // POST /home/cmd -> a control command (rung 2 wires to the device); ack with the dashboard. 57 if m[0] == 2 { 58 if hub_match(req, poff[0], plen[0], "/home/cmd" as *u8) == 1 { 59 hub_puts("[hub] POST /home/cmd (local control; rung-2 wires to device)\n" as *u8) 60 let v: i64 = nx_http_write_response(cfd, 200, ct, ctn, page, page_n) 61 sys_close(cfd) 62 return v 63 } 64 } 65 // GET / (or anything else) -> the dashboard 66 let v2: i64 = nx_http_write_response(cfd, 200, ct, ctn, page, page_n) 67 sys_close(cfd) 68 return v2 69} 70 71func main(argc: i64, argv: *i64) -> i64 { 72 if argc < 2 { 73 hub_puts("usage: nx_iot_hub_serve serve <port> (LAN IoT hub for the Nishi browser)\n" as *u8) 74 return 2 75 } 76 // render the dashboard once at startup (the hub re-renders per tenant in rung 4) 77 let page: *u8 = sys_mmap(HUB_PAGE_CAP) 78 let page_n: i64 = db_render(page) 79 80 let mode: *u8 = argv[1] as *u8 81 // default port 18099; parse argv[2] if present 82 var port: i64 = HUB_MAGIC_18099 83 if argc >= 3 { 84 let ps: *u8 = argv[2] as *u8 85 var p: i64 = 0; var acc: i64 = 0 86 while ps[p] != (0 as u8) { acc = acc * 10 + ((ps[p] as i64) - 48); p = p + 1 } 87 if acc > 0 { port = acc } 88 } 89 90 // serve <port>: bind 0.0.0.0 (LAN reachable from any house device/browser), accept loop. 91 if mode[0] == 115 { // 's' = serve 92 let addr: *u8 = sys_mmap(16) 93 nx_http_server_addr_any(addr, port) 94 let vbox: *i64 = sys_mmap(8) as *i64 95 let lfd: i64 = nx_http_server_listen(addr, 16, vbox) 96 if lfd < 0 { 97 hub_puts("[hub] listen FAILED verdict=" as *u8); hub_puts(nxs_verdict_name(vbox[0])); hub_puts("\n" as *u8) 98 return 3 99 } 100 hub_puts("[hub] sovereign IoT hub serving on 0.0.0.0:" as *u8) 101 // print port 102 let pb: *u8 = sys_mmap(16); var k: i64 = 0; var t: i64 = port; let tmp: *u8 = sys_mmap(16); var z: i64 = 0 103 if t == 0 { tmp[0] = 48; z = 1 } 104 while t > 0 { tmp[z] = (48 + (t % 10)) as u8; t = t / 10; z = z + 1 } 105 while z > 0 { pb[k] = tmp[z-1]; k = k + 1; z = z - 1 } 106 sys_write(1, pb, k) 107 hub_puts(" (Nishi browser: open http://<router-lan-ip>:" as *u8); sys_write(1, pb, k); hub_puts("/ )\n" as *u8) 108 var go: i64 = 1 109 while go == 1 { 110 let avb: *i64 = sys_mmap(8) as *i64 111 let cfd: i64 = nx_http_server_accept_one(lfd, avb) 112 if cfd >= 0 { hub_serve_client(cfd, page, page_n) } 113 } 114 return 0 115 } 116 hub_puts("usage: nx_iot_hub_serve serve <port>\n" as *u8) 117 return 2 118}