- docs/examples/shop: types.wo model, per-feature view modules
(render() classes), root controller files, layout shell/header/
footer, static assets controller + real style.css, README with
Angular file map + run + DX referendum notes
- buy flow proven by hand: stock check/decrement, 409s, traversal
404, css typed, WAL-durable; builds on the UNPATCHED toolchain
- two language gaps recorded, not fixed (per directive): pub+@table
cannot combine (controllers forced into root module); @view
projection classes absent
- story 37 REDIRECTED per Vue-SFC review: view.html compiled by woc
({{}} auto-escaped, w:if/w:for, typed against view class, no
runtime engine); render()-as-concatenation failed the referendum;
forks revised; shop named the acceptance consumer
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
33 lines
1.2 KiB
Text
33 lines
1.2 KiB
Text
-- static_files/controller.wo — serves /assets/* from disk. Traversal-
|
|
-- safe (any ".." answers 404, never touches the filesystem), extension-
|
|
-- mapped content types, 2 MiB cap per file. Text is binary-safe, so
|
|
-- images travel as-is. (Story 38 lifts this into the framework; until
|
|
-- then the template carries its own copy — it is ~40 lines.)
|
|
use framework/http
|
|
use fs
|
|
|
|
pub class StaticFiles {
|
|
dir: Text
|
|
fn handle(req: Req) -> Resp {
|
|
let rel = req.params["path"];
|
|
if rel == nil {
|
|
return not_found();
|
|
}
|
|
if index_of("${rel}", "..") != -1 {
|
|
return not_found();
|
|
}
|
|
let body = try fs.read_all("${self.dir}/${rel}", 2097152) catch (e) nil;
|
|
if body == nil {
|
|
return not_found();
|
|
}
|
|
let ct = "application/octet-stream";
|
|
if ends_with("${rel}", ".css") { ct = "text/css; charset=utf-8"; }
|
|
if ends_with("${rel}", ".js") { ct = "text/javascript"; }
|
|
if ends_with("${rel}", ".svg") { ct = "image/svg+xml"; }
|
|
if ends_with("${rel}", ".png") { ct = "image/png"; }
|
|
if ends_with("${rel}", ".webp") { ct = "image/webp"; }
|
|
let h: map<Text, Text> = {};
|
|
h["content-type"] = ct;
|
|
return Resp { status: 200, headers: h, body: "${body}" };
|
|
}
|
|
}
|