XENOS/src/index_handler.erl

91 lines
3.8 KiB
Erlang

%% index_handler.erl
-module(index_handler).
-behaviour(cowboy_handler).
-include("db_safe_insert.hrl").
-export([init/2]).
init(Req, State) ->
{Body, Req2} = read_full_body(Req),
case json_validate:validate_activity(Body) of
ok ->
%% Decodifica il JSON già validato
Activity0 = jsx:decode(Body, [return_maps]),
Host = cowboy_req:host(Req2),
Activity = ensure_id(Activity0, Host),
To = maps:get(<<"to">>, Activity, []),
case users_local_check:has_local_recipient(To) of
true ->
log_handler:error("Delivered ~p", [Req2]),
cowboy_req:reply(200, #{}, <<"Delivered to local user">>, Req2),
{ok, Req2, State};
false ->
%% SAFE INSERT NELLA GLOBAL INBOX
InsertResult = db_safe_insert:safe_insert(global_message, #global_message{
id = make_ref(),
activity = Activity,
timestamp = erlang:system_time(second)
}),
case InsertResult of
ok ->
log_handler:error("Saved in INBOX database ~p", [Req2]),
cowboy_req:reply(202, #{}, <<"Saved to global inbox">>, Req2),
{ok, Req2, State};
{aborted, Reason} ->
logger:error("DB aborted: ~p", [Reason]),
cowboy_req:reply(500, #{}, <<"Database error">>, Req2),
{ok, Req2, State};
Other ->
logger:error("DB unknown error: ~p", [Other]),
cowboy_req:reply(500, #{}, <<"Unknown DB error">>, Req2),
{ok, Req2, State}
end
end;
{error, malformed_json} ->
log_handler:error("Malformed JSON ricevuto da ~p", [Req2]),
cowboy_req:reply(400, #{}, <<"Malformed JSON">>, Req2),
{ok, Req2, State};
{error, missing_type} ->
cowboy_req:reply(400, #{}, <<"Missing 'type' field">>, Req2),
{ok, Req2, State};
{error, {unsupported_type, TypeBin}} ->
Msg = <<"Unsupported type: ", TypeBin/binary>>,
cowboy_req:reply(400, #{}, Msg, Req2),
{ok, Req2, State};
{error, invalid_type_field} ->
cowboy_req:reply(400, #{}, <<"Invalid 'type' field">>, Req2),
{ok, Req2, State};
{error, Errors} ->
ErrorMsg = io_lib:format("Invalid ActivityPub message: ~p", [Errors]),
cowboy_req:reply(400, #{}, list_to_binary(ErrorMsg), Req2),
{ok, Req2, State}
end.
%% Funzione di utilità per leggere tutto il body
read_full_body(Req) ->
case cowboy_req:read_body(Req) of
{ok, Body, Req2} ->
{Body, Req2};
{more, Data, Req2} ->
{Rest, FinalReq} = read_full_body(Req2),
{<<Data/binary, Rest/binary>>, FinalReq}
end.
ensure_id(Activity, Host) ->
case maps:is_key(<<"id">>, Activity) of
true -> Activity;
false ->
UUID = uuid_v4(),
UUIDBin = list_to_binary(UUID),
Id = <<"https://", Host/binary, "/activity/", UUIDBin/binary>>,
maps:put(<<"id">>, Id, Activity)
end.
uuid_v4() ->
<<A:32, B:16, C:16, D:16, E:48>> = crypto:strong_rand_bytes(16),
% Imposta i bit di versione e variant secondo lo standard UUID v4
C1 = (C band 16#0fff) bor 16#4000,
D1 = (D band 16#3fff) bor 16#8000,
lists:flatten(
io_lib:format("~8.16.0b-~4.16.0b-~4.16.0b-~4.16.0b-~12.16.0b", [A, B, C1, D1, E])
).