1import filepath
2import gleam/bool
3import gleam/bytes_builder
4import gleam/http/request.{type Request, Request}
5import gleam/http/response.{type Response}
6import gleam/httpc
7import gleam/option.{type Option, None, Some}
8import gleam/result
9import gleam/string
10import gleam/uri.{type Uri}
11import glint
12import lustre_dev_tools/cli.{type Cli, do}
13import lustre_dev_tools/cli/flag
14import lustre_dev_tools/error.{IncompleteProxy, InvalidProxyTarget}
15import mist
16import tom
17
18// TYPES -----------------------------------------------------------------------
19
20pub type Proxy {
21 Proxy(from: String, to: Uri)
22}
23
24//
25
26pub fn middleware(
27 req: Request(mist.Connection),
28 proxy: Option(Proxy),
29 k: fn() -> Response(mist.ResponseData),
30) -> Response(mist.ResponseData) {
31 case proxy {
32 None -> k()
33 Some(Proxy(from, to)) -> {
34 use <- bool.lazy_guard(!string.starts_with(req.path, from), k)
35 let internal_error =
36 response.new(500)
37 |> response.set_body(mist.Bytes(bytes_builder.new()))
38
39 let assert Some(host) = to.host
40 let path =
41 req.path
42 |> string.replace(from, "")
43 |> filepath.join(to.path, _)
44 let assert Ok(req) = mist.read_body(req, 100 * 1024 * 1024)
45
46 Request(..req, host: host, port: to.port, path: path)
47 |> httpc.send_bits
48 |> result.map(response.map(_, bytes_builder.from_bit_array))
49 |> result.map(response.map(_, mist.Bytes))
50 |> result.unwrap(internal_error)
51 }
52 }
53}
54
55pub fn get() -> Cli(Option(Proxy)) {
56 use from <- do(get_proxy_from())
57 use to <- do(get_proxy_to())
58
59 case from, to {
60 Some(from), Some(to) -> cli.return(Some(Proxy(from, to)))
61 Some(_), None -> cli.throw(IncompleteProxy(["proxy-to"]))
62 None, Some(_) -> cli.throw(IncompleteProxy(["proxy-from"]))
63 None, None -> cli.return(None)
64 }
65}
66
67fn get_proxy_from() -> Cli(Option(String)) {
68 use flags <- do(cli.get_flags())
69 use config <- do(cli.get_config())
70
71 let flag = result.nil_error(glint.get_flag(flags, flag.proxy_from()))
72 let toml =
73 result.nil_error(
74 tom.get_string(config.toml, ["lustre-dev", "start", "proxy", "from"]),
75 )
76
77 result.or(flag, toml)
78 |> option.from_result
79 |> cli.return
80}
81
82fn get_proxy_to() -> Cli(Option(Uri)) {
83 use flags <- do(cli.get_flags())
84 use config <- do(cli.get_config())
85
86 let flag = result.nil_error(glint.get_flag(flags, flag.proxy_to()))
87 let toml =
88 result.nil_error(
89 tom.get_string(config.toml, ["lustre-dev", "start", "proxy", "to"]),
90 )
91
92 let from = result.or(flag, toml)
93 use <- bool.guard(from == Error(Nil), cli.return(None))
94 let assert Ok(from) = from
95
96 case uri.parse(from) {
97 Ok(from) -> cli.return(Some(from))
98 Error(_) -> cli.throw(InvalidProxyTarget(from))
99 }
100}