diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index 5fa96763..4434de68 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -2566,9 +2566,50 @@ let http_mode port = in write_response fd (http_response ~content_type:"text/plain; charset=utf-8" result); true end else + (* Handler endpoints: paths containing "(api." are handler calls, + not page renders. Evaluate the handler directly, return fragment. *) + let is_handler_path = + let rec has_sub s sub i = + if i + String.length sub > String.length s then false + else if String.sub s i (String.length sub) = sub then true + else has_sub s sub (i + 1) in + has_sub path "(api." 0 in let is_sx = path = "/sx/" || path = "/sx" || (String.length path > 4 && String.sub path 0 4 = "/sx/") in - if is_sx then begin + if is_sx && is_handler_path then begin + (* Handler dispatch — evaluate handler, return raw fragment *) + let response = + try + let api_fn = env_get env "api" in + (* Extract handler slug from path: ...api.SLUG)... *) + let slug = + let rec find_api s i = + if i + 5 > String.length s then "" + else if String.sub s i 5 = "(api." then + let start = i + 5 in + let end_ = try String.index_from s start ')' with Not_found -> String.length s in + String.sub s start (end_ - start) + else find_api s (i + 1) in + find_api path 0 in + let result = Sx_ref.cek_call api_fn (List [String slug]) in + (* Render to SX wire format or HTML depending on content type *) + let body_str = match result with + | String s | SxExpr s -> s + | Nil -> "" + | _ -> + let call = List [Symbol "aser"; List [Symbol "quote"; result]; Env env] in + (match Sx_ref.eval_expr call (Env env) with + | String s | SxExpr s -> s + | v -> Sx_types.inspect v) in + http_response ~content_type:"text/sx; charset=utf-8" body_str + with e -> + Printf.eprintf "[handler] Error for %s: %s\n%!" path (Printexc.to_string e); + http_response ~status:500 ~content_type:"text/sx; charset=utf-8" + (Printf.sprintf "(div :class \"p-4 text-rose-600\" \"Handler error: %s\")" + (escape_sx_string (Printexc.to_string e))) + in + write_response fd response; true + end else if is_sx then begin let cache_key = if is_ajax then "ajax:" ^ path else path in match Hashtbl.find_opt response_cache cache_key with | Some cached -> write_response fd cached; true