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}