aboutsummaryrefslogtreecommitdiff
path: root/bin/main.ml
blob: 29dea845e89ca18e5f65e49e0bc9c2fdf7e0d642 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
open Wormhole

let (fake_post : Post.t) =
  {
    link = "https://mccd.space";
    summary = "My personal blog";
    tags = [ "cool"; "article" ];
    published = "2020-01-01T00:00:00Z";
    author = "Marc";
  }

let (fake_post_2 : Post.t) =
  {
    link = "https://google.com";
    summary = "Some other cool article that I just made";
    tags = [ "cool"; "something" ];
    published = "2020-01-02T00:00:00Z";
    author = "Bob";
  }

let webfinger =
  {|
{
	"subject": "acct:wormhole@galaxy.mccd.space",

	"links": [
		{
			"rel": "self",
			"type": "application/activity+json",
			"href": "https://galaxy.mccd.space/actor"
		}
	]
}
|}

let actor =
  {|
{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1"
  ],

  "id": "https://galaxy.mccd.space/actor",
  "type": "Person",
  "preferredUsername": "wormhole",
  "inbox": "https://galaxy.mccd.space/inbox",

  "publicKey": {
    "id": "https://galaxy.mccd.space/actor#main-key",
    "owner": "https://galaxy.mccd.space/actor",
    "publicKeyPem": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvqa9W2PjNYB6FDuRewSR\nAUyH2TK5iprbfuKWvCGEYq2FRccjmoluMEb0a16AyqqMeZ8J+pI7cPvqpdXm/VVV\niZx3Q5W4H8kIC3I84qAAzVs3wOQWnudk+D+hEE1Il9+yFBFvF7IoyER9axqJEb88\nYQ5okLU/346SMpMrk4wsUFnwaxdVXQPBQ0tVxqVJiLGSMlGXX/1Vl0+lnhgg+5rH\n8rfIbFX4qQC/gYbEU+VS2nzhYjdqn0maL94OrFHYNdrMBgSBOtbBFSJ+kMgojqES\n7Xhf+G9JcuCIqm0T3dHqo50MUSx8lrS78S3uO7WgPAby8qXjcL8sQEfNvJT16sjk\ndwIDAQAB\n-----END PUBLIC KEY-----"
  }
}
  |}

let actor_whitelist =
  [
    "https://fosstodon.org/users/marcc";
    "https://universeodon.com/users/icecreambook";
    "https://mastodon.social/users/ronent";
    "https://graphics.social/users/theohonohan";
  ]

let () =
  let port =
    Sys.getenv_opt "PORT" |> Option.map int_of_string
    |> Option.value ~default:8080
  in
  let env = Sys.getenv_opt "ENV" |> Option.value ~default:"PROD" in
  let disable_auth =
    Sys.getenv_opt "DISABLE_AUTH" |> Option.value ~default:"false"
  in
  let interface = if env = "DEV" then "localhost" else "0.0.0.0" in
  if env = "DEV" then Post.add fake_post;
  if env = "DEV" then Post.add fake_post;
  if env = "DEV" then Post.add fake_post;
  if env = "DEV" then Post.add fake_post;
  Dream.run ~port ~interface @@ Dream.logger
  @@ Dream.router
       [
         Dream.get "/static/**" (Dream.static "./static");
         Dream.get "/feed.xml" (fun _ ->
             let posts = Post.get_all () in
             let maybe_latest_post = Post.latest_post () in
             match maybe_latest_post with
             | Some latest_post ->
                 let rss_posts = posts |> List.map Post.to_rss_entry in
                 let rss =
                   Xml.format_rss "https://galaxy.mccd.space"
                     (Post.published latest_post)
                     rss_posts
                 in
                 Dream.respond rss
                   ~headers:[ ("Content-Type", "application/rss+xml") ]
             | None -> Dream.html "No posts hav been published!");
         Dream.get "/actor" (fun _ ->
             Dream.log "Sending actor";
             Dream.respond actor
               ~headers:[ ("Content-Type", "application/activity+json") ]);
         Dream.get "/.well-known/webfinger" (fun _ ->
             Dream.log "Sending webfinger";
             Dream.respond webfinger
               ~headers:[ ("Content-Type", "application/activity+json") ]);
         Dream.get "/" (fun request ->
             Dream.log "Sending greeting to %s!" (Dream.client request);
             let posts = Post.get_all () in
             Template.render posts |> Dream.html);
         Dream.post "/inbox" (fun request ->
             let%lwt body = Dream.body request in
             Dream.log "Got body: %s" body;
             let signature = Dream.headers request "signature" in
             Dream.log "Got signature: %s" (String.concat " " signature);
             let message_object =
               Yojson.Safe.from_string body |> Post.mastodon_post_of_yojson
             in
             let%lwt actor =
               User.get_user (Post.mastodon_actor message_object)
             in
             match actor with
             | Error e ->
                 Dream.log "User not found %s" (Printexc.to_string e);
                 let code = Some 400 in
                 Dream.json ?code "User not found"
             | Ok actor ->
                 Dream.log "User found";
                 let pem = User.get_public_pem actor |> Result.to_option in
                 let%lwt valid_request = Sig.verify_request pem request in
                 (match valid_request with
                 | Error e ->
                     Dream.log "Error verifying request %s"
                       Printexc.(to_string e);
                     let code = Some 500 in
                     Dream.json ?code "Invalid request"
                 | Ok false ->
                     Dream.log "Unauthorized request";
                     let code = Some 501 in
                     Dream.json ?code "Unauthorized"
                 | Ok true ->
                     message_object
                     |> Post.post_of_mastodon_post (User.name actor)
                     |> Post.add;
                     message_object |> Post.yojson_of_mastodon_post
                     |> Yojson.Safe.to_string |> Dream.log "Added post %s";
                     Dream.json "Added user"));
       ]