Import the initial dudeswave code.

At this point it handles only GETs, giving back plain text
responses in case of errors.

The intention is to also produce some static pages for those,
where the CSS we will chose applies.

Other APIs will be managed through their respective handlers,
whose routes will be setup before this one.
main
absc 2024-07-23 22:52:57 +02:00
parent 14e6c3cc94
commit 1f243b16d3
8 changed files with 260 additions and 0 deletions

7
dudeswave/Makefile Normal file
View File

@ -0,0 +1,7 @@
.PHONY: all clean
all:
${MAKE} -C src
clean:
${MAKE} -C src clean

View File

@ -0,0 +1,12 @@
{application,dudeswave,
[{description,"The dudeswave web experience"},
{vsn,"1.0.0"},
{modules,[dudeswave,dudeswave_app,dudeswave_handler,dudeswave_supervisor]},
{registered,[]},
{applications,[kernel,stdlib,erts,cowboy,ranch]},
{mod,{dudeswave_app,[]}},
{env, [
{ip,"127.0.0.1"},
{port,8080},
]},
{start_phases,[]}]}.

View File

@ -0,0 +1,15 @@
{application,dudeswave,
[{description,"The dudeswave web experience"},
{vsn,"1.0.0"},
{modules,[dudeswave,dudeswave_app,bigweb_handler,bigweb_supervisor]},
{registered,[]},
{applications,[kernel,stdlib,erts,cowboy,ranch]},
{mod,{bigweb_app,[]}},
{env, [
{proxy_host,"gemini.abiscuola.com"},
{ip,"127.0.0.1"},
{port,10443},
{certfile, "/var/_erlang/cert/www.abiscuola.com.crt"},
{keyfile, "/var/_erlang/private/www.abiscuola.com.key"}
]},
{start_phases,[]}]}.

16
dudeswave/src/Makefile Normal file
View File

@ -0,0 +1,16 @@
.PHONY: all clean
.SUFFIXES: .erl .beam
ERLC?= erlc -server
OBJS= dudeswave.beam dudeswave_app.beam
OBJS+= dudeswave_supervisor.beam dudeswave_handler.beam
all: ${OBJS}
.erl.beam:
${ERLC} ${ERLOPTS} ${ERLFLAGS} $<
clean:
rm -f *.beam

View File

@ -0,0 +1,46 @@
%
% Copyright (c) 2024 Andrea Biscuola <a@abiscuola.com>
%
% Permission to use, copy, modify, and distribute this software for any
% purpose with or without fee is hereby granted, provided that the above
% copyright notice and this permission notice appear in all copies.
%
% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
%
-module(dudeswave).
-behaviour(gen_server).
% Server start function
-export([start_link/0]).
% Module callbacks
-export([init/1, handle_call/3, handle_cast/2, terminate/2]).
%
% Startup functions
%
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
process_flag(trap_exit, true),
{ok, 0}.
%
% Callbacks
%
handle_call(_Msg, _From, State) -> {noreply, State}.
handle_cast(_Msg, State) -> {noreply, State}.
terminate(_Reason, _N) -> ok.

View File

@ -0,0 +1,44 @@
%
% Copyright (c) 2024 Andrea Biscuola <a@abiscuola.com>
%
% Permission to use, copy, modify, and distribute this software for any
% purpose with or without fee is hereby granted, provided that the above
% copyright notice and this permission notice appear in all copies.
%
% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
%
-module(dudeswave_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_Type, StartArgs) ->
{ok, Addr} = case application:get_env(ip) of
{ok, AddrConf} -> inet:parse_address(AddrConf);
undefined -> undefined
end,
{ok, Port} = application:get_env(port),
{ok, Bucket} = application:get_env(bucket),
Dispatch = cowboy_router:compile([
{'_', [{"/", dudeswave_handler, #{bucket => Bucket}}]}
]),
{ok, ListenerPid} = cowboy:start_tls(dudeswave_listener, [
{port, Port},
{ip, Addr}
], #{env => #{dispatch => Dispatch}}),
link(ListenerPid),
dudeswave_supervisor:start_link(StartArgs).
stop(_State) ->
cowboy:stop_listener(dudeswave_listener).

View File

@ -0,0 +1,89 @@
%
% Copyright (c) 2024 Andrea Biscuola <a@abiscuola.com>
%
% Permission to use, copy, modify, and distribute this software for any
% purpose with or without fee is hereby granted, provided that the above
% copyright notice and this permission notice appear in all copies.
%
% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
%
-module(dudeswave_handler).
-moduledoc """
The dudeswave GET handler.
This module handles only fetching and returning resources. Anything
else is considered a bad request, apart from the obvious case of 500
(internal server error) and 404 (not found).
""".
-behaviour(cowboy_handler).
-export([init/2, terminate/3]).
-include_lib("storage/include/storage.hrl").
%
% Protocol functions
%
init(Req, State) ->
{ok, Bucket} = maps:find(bucket, State),
Path = cowboy_req:path(Req),
case cowboy_req:method(Req) of
<<"GET">> ->
%
% Fetch the page from storage
%
case storage:read(Bucket, Path) of
{ok, []} ->
Resp = cowboy_req:reply(404,
#{<<"content-type">> => <<"text/plain">>}, <<"Not found">>, Req),
{ok, Resp, State};
%
% If we get a resource, send it back to the client with
% it's proper mime type.
%
{ok, [R]} ->
{Mime, Bin} = get_final_object(R),
Resp = cowboy_req:reply(200, #{<<"content-type">> => Mime},
Bin, Req),
{ok, Resp, State};
{error, _} ->
Resp = cowboy_req:reply(500, #{<<"content-type">> => <<"text/plain">>},
<<"Internal server error">>, Req),
{ok, Resp, State}
end;
_ ->
Resp = cowboy_req:reply(400, #{<<"content-type">> => <<"text/plain">>},
<<"Bad request">>, Req),
{ok, Resp, State}
end.
terminate(_Reason, _Req, _State) -> ok.
-spec get_final_object(Record) -> {Mime, Bin} when
Record :: storage:object(),
Mime :: binary(),
Bin :: binary().
get_final_object(Record) ->
case lists:keyfind(mime, 1, Record#object.metadata) of
%
% We do not know the object mime type. This should never
% happen.
%
false ->
{<<"application/octect-stream">>, Record#object.value};
%
% Everything else, just return it as is.
%
{mime, Mime} ->
{list_to_binary(Mime), Record#object.value}
end.

View File

@ -0,0 +1,31 @@
%
% Copyright (c) 2024 Andrea Biscuola <a@abiscuola.com>
%
% Permission to use, copy, modify, and distribute this software for any
% purpose with or without fee is hereby granted, provided that the above
% copyright notice and this permission notice appear in all copies.
%
% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
%
-module(dudeswave_supervisor).
-behaviour(supervisor).
-export([start/0,
start_link/1,
init/1]).
start() ->
spawn(fun() -> supervisor:start_link({local, ?MODULE}, ?MODULE, _Arg = []) end).
start_link(Args) ->
supervisor:start_link({local, ?MODULE}, ?MODULE, Args).
init([]) ->
{ok, {{one_for_one, 3, 10}, [{tag1, {dudeswave, start_link, []}, permanent,
10000, worker, [dudeswave]}]}}.