First code
parent
f2738c4ae8
commit
b8d35fcc67
|
@ -0,0 +1,34 @@
|
|||
# File di build e compilazione
|
||||
/_build/
|
||||
*.beam
|
||||
*.o
|
||||
*.plt
|
||||
|
||||
# Dipendenze scaricate
|
||||
/deps/
|
||||
|
||||
# File di test e log
|
||||
.eunit/
|
||||
logs/
|
||||
erl_crash.dump
|
||||
|
||||
# File generati da strumenti
|
||||
.rebar/
|
||||
.rebar3/
|
||||
.cover/
|
||||
*.d
|
||||
|
||||
# File temporanei di Mnesia (database locale)
|
||||
Mnesia.*
|
||||
|
||||
# File temporanei di editor
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# File temporanei di sistema
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Escludi solo i file beam da ebin, NON l'intera cartella (serve per i .app)
|
||||
ebin/*.beam
|
|
@ -0,0 +1,4 @@
|
|||
{deps, [
|
||||
{cowboy, "2.10.0"},
|
||||
{jsx, "3.1.0"}
|
||||
]}.
|
|
@ -0,0 +1,17 @@
|
|||
{"1.2.0",
|
||||
[{<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.10.0">>},0},
|
||||
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.12.1">>},1},
|
||||
{<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},0},
|
||||
{<<"ranch">>,{pkg,<<"ranch">>,<<"1.8.0">>},1}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"cowboy">>, <<"FF9FFEFF91DAE4AE270DD975642997AFE2A1179D94B1887863E43F681A203E26">>},
|
||||
{<<"cowlib">>, <<"A9FA9A625F1D2025FE6B462CB865881329B5CAFF8F1854D1CBC9F9533F00E1E1">>},
|
||||
{<<"jsx">>, <<"D12516BAA0BB23A59BB35DCCAF02A1BD08243FCBB9EFE24F2D9D056CCFF71268">>},
|
||||
{<<"ranch">>, <<"8C7A100A139FD57F17327B6413E4167AC559FBC04CA7448E9BE9057311597A1D">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"cowboy">>, <<"3AFDCCB7183CC6F143CB14D3CF51FA00E53DB9EC80CDCD525482F5E99BC41D6B">>},
|
||||
{<<"cowlib">>, <<"163B73F6367A7341B33C794C4E88E7DBFE6498AC42DCD69EF44C5BC5507C8DB0">>},
|
||||
{<<"jsx">>, <<"0C5CC8FDC11B53CC25CF65AC6705AD39E54ECC56D1C22E4ADB8F5A53FB9427F3">>},
|
||||
{<<"ranch">>, <<"49FBCFD3682FAB1F5D109351B61257676DA1A2FDBE295904176D5E521A2DDFE5">>}]}
|
||||
].
|
|
@ -0,0 +1,20 @@
|
|||
{application, activitypub_server,
|
||||
[
|
||||
{description, "A simple ActivityPub server in Erlang"},
|
||||
{vsn, "0.1.0"},
|
||||
{modules, [
|
||||
activitypub_server_app,
|
||||
activitypub_server_sup,
|
||||
hello_handler,
|
||||
webfinger_handler
|
||||
]},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib,
|
||||
cowboy,
|
||||
jsx
|
||||
]},
|
||||
{mod, {activitypub_server_app, []}},
|
||||
{env, []}
|
||||
]}.
|
|
@ -0,0 +1,31 @@
|
|||
%% activitypub_server_app.erl
|
||||
%% Modulo principale dell'applicazione OTP
|
||||
|
||||
-module(activitypub_server_app).
|
||||
-behaviour(application).
|
||||
|
||||
%% Esporta le funzioni richieste dal behaviour application
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
start(_Type, _Args) ->
|
||||
|
||||
%% creiamo un utente di test, solo per vedere se mnesia va
|
||||
user_db:init(),
|
||||
user_db:add_user(<<"admin">>, #{email => <<"admin@example.com">>}),
|
||||
|
||||
Dispatch = cowboy_router:compile([
|
||||
{'_', [
|
||||
{"/", hello_handler, []},
|
||||
{"/.well-known/webfinger", webfinger_handler, []}
|
||||
]}
|
||||
]),
|
||||
{ok, _} = cowboy:start_clear(
|
||||
http_listener,
|
||||
[{port, 8080}],
|
||||
#{env => #{dispatch => Dispatch}}
|
||||
),
|
||||
activitypub_server_sup:start_link().
|
||||
|
||||
|
||||
stop(_State) ->
|
||||
ok.
|
|
@ -0,0 +1,13 @@
|
|||
-module(activitypub_server_sup).
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([start_link/0]).
|
||||
-export([init/1]).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
|
||||
init([]) ->
|
||||
{ok, {{one_for_one, 1, 5}, []}}.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
-module(hello_handler).
|
||||
-behaviour(cowboy_handler).
|
||||
|
||||
-export([init/2]).
|
||||
|
||||
init(Req, State) ->
|
||||
{ok, Resp} = cowboy_req:reply(
|
||||
200,
|
||||
#{<<"content-type">> => <<"text/plain">>},
|
||||
<<"Hello, world!">>,
|
||||
Req
|
||||
),
|
||||
{ok, Resp, State}.
|
|
@ -0,0 +1,5 @@
|
|||
-module(start_server).
|
||||
-export([main/0]).
|
||||
|
||||
main() ->
|
||||
application:ensure_all_started(activitypub_server).
|
|
@ -0,0 +1,35 @@
|
|||
-module(user_db).
|
||||
-export([init/0, add_user/2, get_user/1, all_users/0]).
|
||||
|
||||
-record(user, {username, data}).
|
||||
|
||||
init() ->
|
||||
mnesia:create_schema([node()]),
|
||||
mnesia:start(),
|
||||
mnesia:create_table(user, [
|
||||
{attributes, record_info(fields, user)},
|
||||
{disc_copies, [node()]}
|
||||
]).
|
||||
|
||||
add_user(Username, Data) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#user{username=Username, data=Data})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
get_user(Username) ->
|
||||
F = fun() ->
|
||||
case mnesia:read({user, Username}) of
|
||||
[User] -> {ok, User};
|
||||
[] -> not_found
|
||||
end
|
||||
end,
|
||||
{atomic, Result} = mnesia:transaction(F),
|
||||
Result.
|
||||
|
||||
all_users() ->
|
||||
F = fun() ->
|
||||
mnesia:match_object(#user{username='_', data='_'})
|
||||
end,
|
||||
{atomic, Users} = mnesia:transaction(F),
|
||||
Users.
|
|
@ -0,0 +1,77 @@
|
|||
%% webfinger_handler.erl
|
||||
%% Handler per l'endpoint /.well-known/webfinger
|
||||
-module(webfinger_handler).
|
||||
-behaviour(cowboy_handler).
|
||||
|
||||
%% Cowboy vuole che esportiamo la funzione init/2
|
||||
-export([init/2]).
|
||||
|
||||
init(Req, State) ->
|
||||
Params = cowboy_req:parse_qs(Req),
|
||||
Resource = proplists:get_value(<<"resource">>, Params),
|
||||
|
||||
case Resource of
|
||||
undefined ->
|
||||
{ok, Resp} = cowboy_req:reply(
|
||||
400,
|
||||
#{<<"content-type">> => <<"application/json">>},
|
||||
<<"{\"error\": \"Missing resource parameter\"}">>,
|
||||
Req
|
||||
),
|
||||
{ok, Resp, State};
|
||||
_ ->
|
||||
case parse_acct(Resource) of
|
||||
{ok, Username, Domain} ->
|
||||
%% Qui interroghiamo Mnesia!
|
||||
case user_db:get_user(Username) of
|
||||
{ok, _User} ->
|
||||
ActorUrl = <<"https://", Domain/binary, "/users/", Username/binary>>,
|
||||
WebfingerMap = #{
|
||||
<<"subject">> => <<"acct:", Username/binary, "@", Domain/binary>>,
|
||||
<<"links">> => [
|
||||
#{
|
||||
<<"rel">> => <<"self">>,
|
||||
<<"type">> => <<"application/activity+json">>,
|
||||
<<"href">> => ActorUrl
|
||||
}
|
||||
]
|
||||
},
|
||||
Json = jsx:encode(WebfingerMap),
|
||||
{ok, Resp} = cowboy_req:reply(
|
||||
200,
|
||||
#{<<"content-type">> => <<"application/jrd+json">>},
|
||||
Json,
|
||||
Req
|
||||
),
|
||||
{ok, Resp, State};
|
||||
not_found ->
|
||||
{ok, Resp} = cowboy_req:reply(
|
||||
404,
|
||||
#{<<"content-type">> => <<"application/json">>},
|
||||
<<"{\"error\": \"User not found\"}">>,
|
||||
Req
|
||||
),
|
||||
{ok, Resp, State}
|
||||
end;
|
||||
error ->
|
||||
{ok, Resp} = cowboy_req:reply(
|
||||
400,
|
||||
#{<<"content-type">> => <<"application/json">>},
|
||||
<<"{\"error\": \"Invalid resource format\"}">>,
|
||||
Req
|
||||
),
|
||||
{ok, Resp, State}
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
%% Funzione di utilità per estrarre username e dominio da "acct:username@domain"
|
||||
parse_acct(<<"acct:", Rest/binary>>) ->
|
||||
case binary:split(Rest, <<"@">>) of
|
||||
[Username, Domain] when Username =/= <<>>, Domain =/= <<>> ->
|
||||
{ok, Username, Domain};
|
||||
_ ->
|
||||
error
|
||||
end;
|
||||
parse_acct(_) ->
|
||||
error.
|
Loading…
Reference in New Issue