From 887059c52f7d8d820688e0e8bac2458d27481651 Mon Sep 17 00:00:00 2001 From: Marco Martins Date: Tue, 24 Oct 2023 21:39:40 +0200 Subject: [PATCH] Create proxy with gleam --- .github/workflows/test.yml | 23 ------- src/cockpit_cms_proxy.gleam | 129 +++++++++++++++++++++++++++++++----- 2 files changed, 113 insertions(+), 39 deletions(-) delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 701b1f5..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: test - -on: - push: - branches: - - master - - main - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: erlef/setup-beam@v1 - with: - otp-version: "26.0.2" - gleam-version: "0.31.0" - rebar3-version: "3" - # elixir-version: "1.15.4" - - run: gleam format --check src test - - run: gleam deps download - - run: gleam test diff --git a/src/cockpit_cms_proxy.gleam b/src/cockpit_cms_proxy.gleam index 64fdf55..ce07f68 100644 --- a/src/cockpit_cms_proxy.gleam +++ b/src/cockpit_cms_proxy.gleam @@ -1,20 +1,35 @@ import gleam/bit_builder import gleam/erlang/process +import gleam/erlang/os import gleam/http/request.{Request} import gleam/http/response.{Response} import mist.{Connection, ResponseData} import gleam/httpc -import gleam/http.{Get, Http} -import gleam/string.{join} +import gleam/http.{Method, Scheme} +import gleam/string +import gleam/io +import gleam/option.{Option, unwrap} +import gleam/result +import gleam/int -fn handle_website(segments: List(String)) -> Response(ResponseData) { +pub type Website { + Website( + target_host: String, + target_port: Int, + scheme: Scheme, + method: Method, + path: String, + ) +} + +fn handle_website(config: Website) -> Response(ResponseData) { let result = request.new() - |> request.set_scheme(Http) - |> request.set_method(Get) - |> request.set_host("localhost") - |> request.set_port(1234) - |> request.set_path(join(segments, "/")) + |> request.set_scheme(config.scheme) + |> request.set_method(config.method) + |> request.set_host(config.target_host) + |> request.set_port(config.target_port) + |> request.set_path(config.path) |> httpc.send case result { @@ -27,15 +42,97 @@ fn handle_website(segments: List(String)) -> Response(ResponseData) { } } +type Image { + Image( + base_url: String, + api_token: String, + path: List(String), + query: Option(String), + ) +} + +fn handle_images(config: Image) -> Response(ResponseData) { + let url = + "/api/cockpit/image?token=" <> config.api_token <> "&src=" <> config.base_url <> "/storage/uploads/" <> string.join( + config.path, + "/", + ) <> "&" <> unwrap(config.query, "") + + let result = + request.new() + |> request.set_host(string.replace(config.base_url, "https://", "")) + |> request.set_path(url) + |> request.set_body(<<>>) + |> httpc.send_bits + + case result { + Ok(r) -> + response.new(200) + |> response.set_body(mist.Bytes(bit_builder.from_bit_string(r.body))) + Error(_) -> + response.new(404) + |> response.set_body(mist.Bytes(bit_builder.from_string("

nope

"))) + } +} + +type Configuration { + Configuration( + base_url: String, + api_token: String, + target_host: String, + target_port: Int, + port: Int, + ) +} + +fn load_configuration() -> Result(Configuration, String) { + case + result.all([ + os.get_env("COCKPIT_BASE_URL"), + os.get_env("COCKPIT_API_TOKEN"), + os.get_env("TARGET_HOST"), + os.get_env("TARGET_PORT"), + os.get_env("PORT"), + ]) + { + Ok([base_url, api_token, target_host, target_p, p]) -> + case result.all([int.parse(target_p), int.parse(p)]) { + Ok([target_port, port]) -> + Ok(Configuration(base_url, api_token, target_host, target_port, port)) + Error(_) -> Error("Port values are not numbers") + } + Error(_) -> Error("Missing environment variables") + } +} + pub fn main() { - fn(req: Request(Connection)) -> Response(ResponseData) { - case request.path_segments(req) { - _ as segments -> handle_website(segments) + case load_configuration() { + Ok(config) -> { + fn(req: Request(Connection)) -> Response(ResponseData) { + case request.path_segments(req) { + ["image", "api", ..path] -> + handle_images(Image( + config.base_url, + config.api_token, + path, + req.query, + )) + _ -> + handle_website(Website( + config.target_host, + config.target_port, + req.scheme, + req.method, + req.path, + )) + } + } + |> mist.new + |> mist.port(config.port) + |> mist.start_http + + process.sleep_forever() } + Error(message) -> io.print_error(message) } - |> mist.new - |> mist.port(8000) - |> mist.start_http - - process.sleep_forever() }