-module(glitch@auth@redirect_server). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([start/1, shutdown/1, new/3]). -export_type([redirect_server/0, status/0, message/0]). -opaque redirect_server() :: {state, binary(), gleam@erlang@process:subject(binary()), gleam@option:option(gleam@erlang@process:subject(gleam@otp@supervisor:message())), gleam@uri:uri(), status()}. -type status() :: running | stopped. -type message() :: shutdown | start. -spec start(gleam@erlang@process:subject(message())) -> nil. start(Server) -> gleam@otp@actor:send(Server, start). -spec shutdown(gleam@erlang@process:subject(message())) -> nil. shutdown(Server) -> gleam@otp@actor:send(Server, shutdown). -spec make_mist_body(gleam@option:option(binary())) -> mist:response_data(). make_mist_body(Body) -> _pipe = Body, _pipe@1 = gleam@option:unwrap(_pipe, <<""/utf8>>), _pipe@2 = gleam_stdlib:wrap_list(_pipe@1), {bytes, _pipe@2}. -spec get_code_and_csrf_token_query_params(gleam@http@request:request(any())) -> {gleam@option:option(binary()), gleam@option:option(binary())}. get_code_and_csrf_token_query_params(Req) -> Query_params = begin _pipe = gleam@http@request:get_query(Req), gleam@result:unwrap(_pipe, []) end, Code_result = gleam@list:key_find(Query_params, <<"code"/utf8>>), Csrf_state_result = gleam@list:key_find(Query_params, <<"state"/utf8>>), {gleam@option:from_result(Code_result), gleam@option:from_result(Csrf_state_result)}. -spec bad_request_response(gleam@option:option(binary())) -> gleam@http@response:response(mist:response_data()). bad_request_response(Message) -> _pipe = gleam@http@response:new(400), gleam@http@response:set_body(_pipe, make_mist_body(Message)). -spec generic_bad_request_response() -> gleam@http@response:response(mist:response_data()). generic_bad_request_response() -> bad_request_response(none). -spec ok_response(gleam@option:option(binary())) -> gleam@http@response:response(mist:response_data()). ok_response(Body) -> _pipe = gleam@http@response:new(200), gleam@http@response:set_body(_pipe, make_mist_body(Body)). -spec send_response(redirect_server(), binary()) -> fun((any()) -> gleam@http@response:response(mist:response_data())). send_response(Server, Code) -> fun(_) -> gleam@erlang@process:send_after(erlang:element(3, Server), 250, Code), ok_response( {some, <<"Successfully received redirect payload from Twitch."/utf8>>} ) end. -spec make_redirect_handler(redirect_server()) -> fun((gleam@http@request:request(mist@internal@http:connection())) -> gleam@http@response:response(mist:response_data())). make_redirect_handler(Server) -> Handle_redirect = fun(Req) -> case get_code_and_csrf_token_query_params(Req) of {{some, Code}, {some, State}} when State =:= erlang:element( 2, Server ) -> _pipe = mist:read_body(Req, 10485760), _pipe@1 = gleam@result:map(_pipe, send_response(Server, Code)), gleam@result:lazy_unwrap( _pipe@1, fun generic_bad_request_response/0 ); {{some, _}, {some, _}} -> bad_request_response({some, <<"Invalid csrf state"/utf8>>}); {{some, _}, none} -> bad_request_response({some, <<"No csrf state received"/utf8>>}); {none, _} -> bad_request_response({some, <<"No code received"/utf8>>}) end end, Handle_redirect. -spec new_router(redirect_server()) -> fun((gleam@http@request:request(mist@internal@http:connection())) -> gleam@http@response:response(mist:response_data())). new_router(Server) -> Redirect_path = erlang:element(6, erlang:element(5, Server)), Router = fun(Req) -> case {erlang:element(2, Req), gleam@http@request:path_segments(Req)} of {get, [Path]} when Path =:= Redirect_path -> (make_redirect_handler(Server))(Req); {_, _} -> _pipe = gleam@http@response:new(404), gleam@http@response:set_body( _pipe, {bytes, gleam@bytes_builder:new()} ) end end, Router. -spec handle_message(message(), redirect_server()) -> gleam@otp@actor:next(message(), redirect_server()). handle_message(Message, State) -> case Message of shutdown -> _assert_subject = begin _pipe = erlang:element(4, State), _pipe@1 = gleam@option:map( _pipe, fun gleam@erlang@process:subject_owner/1 ), gleam@option:map(_pipe@1, fun gleam@erlang@process:kill/1) end, {some, nil} = case _assert_subject of {some, nil} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"glitch/auth/redirect_server"/utf8>>, function => <<"handle_message"/utf8>>, line => 50}) end, {stop, normal}; start -> Port = gleam@option:unwrap( erlang:element(5, erlang:element(5, State)), 3000 ), _assert_subject@1 = begin _pipe@2 = mist:new(new_router(State)), _pipe@3 = mist:port(_pipe@2, Port), mist:start_http(_pipe@3) end, {ok, Mist_subject} = case _assert_subject@1 of {ok, _} -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"glitch/auth/redirect_server"/utf8>>, function => <<"handle_message"/utf8>>, line => 60}) end, gleam@otp@actor:continue( erlang:setelement( 6, erlang:setelement(4, State, {some, Mist_subject}), running ) ) end. -spec new(binary(), gleam@erlang@process:subject(binary()), gleam@uri:uri()) -> {ok, gleam@erlang@process:subject(message())} | {error, gleam@otp@actor:start_error()}. new(Csrf_state, Mailbox, Redirect_uri) -> State = {state, Csrf_state, Mailbox, none, Redirect_uri, stopped}, gleam@otp@actor:start(State, fun handle_message/2).