First code

main
Uriel Fanelli 2025-05-10 20:08:41 +02:00
parent f2738c4ae8
commit b8d35fcc67
10 changed files with 249 additions and 0 deletions

34
.gitignore vendored Normal file
View File

@ -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

4
rebar.config Normal file
View File

@ -0,0 +1,4 @@
{deps, [
{cowboy, "2.10.0"},
{jsx, "3.1.0"}
]}.

17
rebar.lock Normal file
View File

@ -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">>}]}
].

View File

@ -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, []}
]}.

View File

@ -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.

View File

@ -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}, []}}.

13
src/hello_handler.erl Normal file
View File

@ -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}.

5
src/start_server.erl Normal file
View File

@ -0,0 +1,5 @@
-module(start_server).
-export([main/0]).
main() ->
application:ensure_all_started(activitypub_server).

35
src/user_db.erl Normal file
View File

@ -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.

77
src/webfinger_handler.erl Normal file
View File

@ -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.