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])
 | |
|             ). |