91 lines
3.8 KiB
Erlang
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])
|
|
). |