code wiki / _hdl_build / nx_aw_devserver.nx

nx_aw_devserver.nx source

↩ module page · 208 lines · 12213 B

1// nx_aw_devserver.nx -- a REAL front-end for the CMS capabilities: a plain-HTTP server (loopback) that 2// ROUTES requests and renders responses LIVE using the actual capability organs -- so the features are 3// finally testable from a browser, not just unit gates. Routes: 4// GET / -> home, rendered from the i18n content store (English) 5// GET /?lang=fr -> SAME page in French (nx_cms_i18n RFC-4647 resolve, live) 6// GET /moderate?text=.. -> live comment moderation (nx_cms_comments score + verdict) 7// GET /price?cents=&qty=&tax= -> live integer-exact money (nx_cms_ecommerce, no float) 8// GET /ab?visitor=.. -> live deterministic A/B variant assignment (nx_cms_abtest) 9// GET /health -> ok 10// Composes nx_http_server (serve) + the capability organs. Plain HTTP for local front-end testing; the 11// live site uses the TLS daemon. license_tier: ORIGINAL 12import "nx_syscalls.nx" 13import "nx_http_server.nx" 14import "nx_cms_i18n.nx" 15import "nx_cms_comments.nx" 16import "nx_cms_ecommerce.nx" 17import "nx_cms_abtest.nx" 18const DS_MAGIC_8192: i64 = 8192 19const DS_MAGIC_2048: i64 = 2048 20const DS_MAGIC_65536: i64 = 65536 21const DS_MAGIC_70000: i64 = 70000 22const DS_MAGIC_100000: i64 = 100000 23const DS_MAGIC_1999: i64 = 1999 24 25const DS_PORT: i64 = 8099 26 27func ds_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 28func ds_app(dst: *u8, o: i64, s: *u8) -> i64 { var p: i64=o; var k: i64=0; while s[k]!=(0 as u8){dst[p]=s[k];p=p+1;k=k+1} return p } 29func ds_appb(dst: *u8, o: i64, src: *u8, n: i64) -> i64 { var p: i64=o; var k: i64=0; while k<n{dst[p]=src[k];p=p+1;k=k+1} return p } 30func ds_appn(dst: *u8, o: i64, v: i64) -> i64 { 31 var p: i64=o; let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m; dst[p]=45 as u8; p=p+1}; var k: i64=0 32 if m==0 {t[0]=48 as u8;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1} 33 var i: i64=0; while i<k {dst[p]=t[k-1-i]; p=p+1; i=i+1} return p 34} 35 36func ds_find(buf: *u8, n: i64, pat: *u8, plen: i64) -> i64 { 37 if plen==0 { return 0-1 } 38 var i: i64=0 39 while i+plen<=n { 40 var j: i64=0; var ok: i64=1 41 while j<plen { if (buf[i+j] as i64)!=(pat[j] as i64){ok=0;j=plen} else {j=j+1} } 42 if ok==1 { return i } 43 i=i+1 44 } 45 return 0-1 46} 47 48// extract the request target (path+query) from "GET <target> HTTP/.." into out; returns length 49func ds_target(req: *u8, rn: i64, out: *u8, cap: i64) -> i64 { 50 var i: i64=0 51 while i<rn { if (req[i] as i64)==32 { break } i=i+1 } 52 i=i+1 53 var o: i64=0 54 while i<rn { let c: i64=req[i] as i64; if c==32 { break } if o<cap-1 { out[o]=req[i]; o=o+1 } i=i+1 } 55 out[o]=0 as u8 56 return o 57} 58 59// does s (sl bytes) start with NUL-terminated pat? 60func ds_starts(s: *u8, sl: i64, pat: *u8) -> i64 { 61 var i: i64=0 62 while pat[i]!=(0 as u8) { if i>=sl { return 0 } if (s[i] as i64)!=(pat[i] as i64){return 0} i=i+1 } 63 return 1 64} 65 66// copy the value of query key (e.g. "text=") into out, '+' -> space, until '&'/end. -1 if absent. 67func ds_qstr(target: *u8, tl: i64, key: *u8, out: *u8, cap: i64) -> i64 { 68 let kl: i64=ds_strlen(key) 69 let at: i64=ds_find(target, tl, key, kl) 70 if at<0 { return 0-1 } 71 var i: i64=at+kl; var o: i64=0 72 while i<tl { let c: i64=target[i] as i64; if c==38 { break } if o<cap-1 { if c==43 { out[o]=32 as u8 } else { out[o]=c as u8 } o=o+1 } i=i+1 } 73 out[o]=0 as u8 74 return o 75} 76 77// parse an unsigned int from query key; -1 if absent, 0 if present-but-empty. 78func ds_qint(target: *u8, tl: i64, key: *u8) -> i64 { 79 let kl: i64=ds_strlen(key) 80 let at: i64=ds_find(target, tl, key, kl) 81 if at<0 { return 0-1 } 82 var i: i64=at+kl; var v: i64=0; var any: i64=0 83 while i<tl { let c: i64=target[i] as i64; if c<48 { break } if c>57 { break } v=v*10+(c-48); any=any+1; i=i+1 } 84 if any==0 { return 0 } 85 return v 86} 87 88// shared page chrome (S-class-ish, single-quoted attrs so no quotes inside Nishi strings) 89func ds_head(body: *u8, o: i64, title: *u8) -> i64 { 90 var p: i64=o 91 p=ds_app(body, p, "<!doctype html><html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>" as *u8) 92 p=ds_app(body, p, title) 93 p=ds_app(body, p, "</title><style>body{font:16px/1.6 system-ui,sans-serif;max-width:760px;margin:40px auto;padding:0 16px;color:#1a1a2e}a{color:#3a6ea5}nav a{margin-right:14px}.card{background:#f6f7fb;border:1px solid #e3e6ef;border-radius:10px;padding:16px 20px;margin:14px 0}.tag{display:inline-block;padding:2px 8px;border-radius:6px;font-size:13px}.ok{background:#dff5e1;color:#1b6b32}.bad{background:#fde0e0;color:#a11}.muted{color:#667}</style></head><body>" as *u8) 94 p=ds_app(body, p, "<nav><a href='/'>Home</a><a href='/?lang=fr'>FR</a><a href='/moderate?text=buy+casino+loan+http://x'>Moderation</a><a href='/price?cents=1999&qty=3&tax=825'>Pricing</a><a href='/ab?visitor=you'>A/B</a></nav><hr>" as *u8) 95 return p 96} 97func ds_foot(body: *u8, o: i64) -> i64 { 98 return ds_app(body, o, "<hr><p class='muted'>Served live by nx_aw_devserver -- each route renders through a real Nishi CMS capability organ (no static HTML).</p></body></html>" as *u8) 99} 100 101func main() -> i64 { 102 // i18n content store (locale-tagged), built once 103 let store: *u8 = sys_mmap(DS_MAGIC_8192) 104 let sn: i64 = ds_app(store, 0, "@title.en\nAndelin West, PLLC\n@title.fr\nAndelin West, SARL\n@intro.en\nSovereign legal services, served from the silicon up.\n@intro.fr\nServices juridiques souverains, du silicium vers le haut.\n@title\nAndelin West\n@intro\nLegal services.\n" as *u8) 105 106 let addr: *u8 = sys_mmap(16) 107 nx_http_server_addr_any(addr, DS_PORT) // 0.0.0.0 -- LAN-reachable so the same binary serves as DEV on the NAS 108 let lv: *i64 = sys_mmap(8) as *i64 109 let lfd: i64 = nx_http_server_listen(addr, 16, lv) 110 if lfd < 0 { sys_write(1, "DEVSERVER listen-fail\n" as *u8, 22); return 4 } 111 sys_write(1, "nx_aw_devserver listening 0.0.0.0:8088 (dev)\n" as *u8, 45) 112 113 // hoisted per-connection buffers 114 let av: *i64 = sys_mmap(8) as *i64 115 let req: *u8 = sys_mmap(DS_MAGIC_8192) 116 let tgt: *u8 = sys_mmap(DS_MAGIC_2048) 117 let arg: *u8 = sys_mmap(DS_MAGIC_2048) 118 let body: *u8 = sys_mmap(DS_MAGIC_65536) 119 let resp: *u8 = sys_mmap(DS_MAGIC_70000) 120 let vbuf: *u8 = sys_mmap(512) 121 122 var served: i64 = 0 123 while served < DS_MAGIC_100000 { 124 let cfd: i64 = nx_http_server_accept_one(lfd, av) 125 if cfd < 0 { served = served + 1; continue } 126 let rn: i64 = sys_read(cfd, req, DS_MAGIC_8192) 127 var tl: i64 = 0 128 if rn > 0 { tl = ds_target(req, rn, tgt, DS_MAGIC_2048) } 129 130 var bo: i64 = 0 131 132 // ---- routing ---- 133 if ds_starts(tgt, tl, "/health" as *u8) == 1 { 134 bo = ds_app(body, bo, "ok" as *u8) 135 } 136 if ds_starts(tgt, tl, "/moderate" as *u8) == 1 { 137 let al: i64 = ds_qstr(tgt, tl, "text=" as *u8, arg, DS_MAGIC_2048) 138 bo = ds_head(body, bo, "Live comment moderation" as *u8) 139 bo = ds_app(body, bo, "<h1>Comment moderation (live)</h1><p class='muted'>Edit <code>?text=</code> in the URL and reload.</p><div class='card'><b>Comment:</b> " as *u8) 140 if al >= 0 { bo = ds_appb(body, bo, arg, al) } else { bo = ds_app(body, bo, "<i>(none)</i>" as *u8) } 141 var sc: i64 = 0 142 if al > 0 { sc = cmt_score(arg, al) } 143 let verdict: i64 = cmt_classify(sc) 144 bo = ds_app(body, bo, "<p>spam score = <b>" as *u8); bo = ds_appn(body, bo, sc) 145 bo = ds_app(body, bo, "</b> (threshold " as *u8); bo = ds_appn(body, bo, NX_CMT_SPAM_THRESHOLD) 146 bo = ds_app(body, bo, ")</p><p>verdict: " as *u8) 147 if verdict == NX_CMT_SPAM { bo = ds_app(body, bo, "<span class='tag bad'>SPAM - withheld from the public page</span>" as *u8) } 148 else { bo = ds_app(body, bo, "<span class='tag ok'>PENDING - queued for moderation (never auto-published)</span>" as *u8) } 149 bo = ds_app(body, bo, "</div>" as *u8) 150 bo = ds_foot(body, bo) 151 } 152 if ds_starts(tgt, tl, "/price" as *u8) == 1 { 153 var cents: i64 = ds_qint(tgt, tl, "cents="); if cents < 0 { cents = DS_MAGIC_1999 } 154 var qty: i64 = ds_qint(tgt, tl, "qty="); if qty < 0 { qty = 1 } 155 var tax: i64 = ds_qint(tgt, tl, "tax="); if tax < 0 { tax = 0 } 156 let line: i64 = ec_line_total(cents, qty) 157 let total: i64 = ec_apply_tax(line, tax) 158 bo = ds_head(body, bo, "Live exact pricing" as *u8) 159 bo = ds_app(body, bo, "<h1>Integer-exact pricing (live)</h1><p class='muted'>Edit <code>?cents=&qty=&tax=</code> (tax in basis points; 825 = 8.25%).</p><div class='card'>" as *u8) 160 bo = ds_app(body, bo, "<p>unit: $" as *u8); bo = ds_appn(body, bo, cents/100); bo = ds_app(body, bo, "." as *u8); if cents%100 < 10 { bo = ds_app(body, bo, "0" as *u8) } bo = ds_appn(body, bo, cents%100) 161 bo = ds_app(body, bo, " &times; " as *u8); bo = ds_appn(body, bo, qty) 162 bo = ds_app(body, bo, "</p><p>line total: $" as *u8); bo = ds_appn(body, bo, line/100); bo = ds_app(body, bo, "." as *u8); if line%100 < 10 { bo = ds_app(body, bo, "0" as *u8) } bo = ds_appn(body, bo, line%100) 163 bo = ds_app(body, bo, "</p><p><b>total with tax: $" as *u8); bo = ds_appn(body, bo, total/100); bo = ds_app(body, bo, "." as *u8); if total%100 < 10 { bo = ds_app(body, bo, "0" as *u8) } bo = ds_appn(body, bo, total%100) 164 bo = ds_app(body, bo, "</b> = " as *u8); bo = ds_appn(body, bo, total); bo = ds_app(body, bo, " cents (exact, no float)</p></div>" as *u8) 165 bo = ds_foot(body, bo) 166 } 167 if ds_starts(tgt, tl, "/ab" as *u8) == 1 { 168 let al: i64 = ds_qstr(tgt, tl, "visitor=" as *u8, arg, DS_MAGIC_2048) 169 var vp: *u8 = "anon" as *u8 170 if al > 0 { vp = arg } 171 let variant: i64 = ab_assign("homepage-cta" as *u8, vp, 2) 172 bo = ds_head(body, bo, "Live A/B assignment" as *u8) 173 bo = ds_app(body, bo, "<h1>A/B variant (live, deterministic)</h1><p class='muted'>Edit <code>?visitor=</code> -- the same visitor always gets the same arm, with no stored state.</p><div class='card'><p>visitor: <b>" as *u8) 174 bo = ds_appb(body, bo, vp, ds_strlen(vp)) 175 bo = ds_app(body, bo, "</b></p><p>assigned arm: " as *u8) 176 if variant == 0 { bo = ds_app(body, bo, "<span class='tag ok'>A &mdash; Get a free consultation</span>" as *u8) } 177 else { bo = ds_app(body, bo, "<span class='tag ok'>B &mdash; Talk to an attorney today</span>" as *u8) } 178 bo = ds_app(body, bo, "</p></div>" as *u8) 179 bo = ds_foot(body, bo) 180 } 181 // home (default): i18n-resolved title + intro 182 if bo == 0 { 183 var locale: *u8 = "en" as *u8 184 if ds_find(tgt, tl, "lang=fr" as *u8, 7) >= 0 { locale = "fr" as *u8 } 185 let tier: *i64 = sys_mmap(16) as *i64 186 let tv: i64 = i18n_resolve(store, sn, "title" as *u8, locale, "en" as *u8, vbuf, 256, tier) 187 bo = ds_head(body, bo, "Andelin West" as *u8) 188 bo = ds_app(body, bo, "<h1>" as *u8); if tv >= 0 { bo = ds_appb(body, bo, vbuf, tv) } 189 bo = ds_app(body, bo, "</h1><p>" as *u8) 190 let iv: i64 = i18n_resolve(store, sn, "intro" as *u8, locale, "en" as *u8, vbuf, 256, tier) 191 if iv >= 0 { bo = ds_appb(body, bo, vbuf, iv) } 192 bo = ds_app(body, bo, "</p><p class='muted'>locale served: " as *u8); bo = ds_appb(body, bo, locale, 2) 193 bo = ds_app(body, bo, " &mdash; try <a href='/?lang=fr'>?lang=fr</a>. Every nav link above is a live capability.</p>" as *u8) 194 bo = ds_foot(body, bo) 195 } 196 197 // ---- response ---- 198 var ro: i64 = ds_app(resp, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8) 199 ro = ds_appn(resp, ro, bo) 200 ro = ds_app(resp, ro, "\r\nConnection: close\r\nX-Served-By: nx_aw_devserver\r\n\r\n" as *u8) 201 ro = ds_appb(resp, ro, body, bo) 202 sys_write(cfd, resp, ro) 203 sys_close(cfd) 204 served = served + 1 205 } 206 sys_close(lfd) 207 return 0 208}