Diffusion: a simple federated social site, built in Gleam
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

stub out some ideas, i suppose

graphiteisaac cac8ab31

+242
+23
.github/workflows/test.yml
··· 1 + name: test 2 + 3 + on: 4 + push: 5 + branches: 6 + - master 7 + - main 8 + pull_request: 9 + 10 + jobs: 11 + test: 12 + runs-on: ubuntu-latest 13 + steps: 14 + - uses: actions/checkout@v4 15 + - uses: erlef/setup-beam@v1 16 + with: 17 + otp-version: "27.1.2" 18 + gleam-version: "1.12.0" 19 + rebar3-version: "3" 20 + # elixir-version: "1" 21 + - run: gleam deps download 22 + - run: gleam test 23 + - run: gleam format --check src test
+4
.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
README.md
··· 1 + # diffusion 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/diffusion)](https://hex.pm/packages/diffusion) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/diffusion/) 5 + 6 + ```sh 7 + gleam add diffusion@1 8 + ``` 9 + ```gleam 10 + import diffusion 11 + 12 + pub fn main() -> Nil { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/diffusion>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+24
gleam.toml
··· 1 + name = "diffusion" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 + gleam_json = ">= 3.0.2 and < 4.0.0" 18 + wisp = ">= 2.1.0 and < 3.0.0" 19 + mist = ">= 5.0.3 and < 6.0.0" 20 + gleam_time = ">= 1.4.0 and < 2.0.0" 21 + 22 + [dev-dependencies] 23 + gleeunit = ">= 1.0.0 and < 2.0.0" 24 + gleam_httpc = ">= 5.0.0 and < 6.0.0"
+39
manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "directories", version = "1.2.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_stdlib", "platform", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "D13090CFCDF6759B87217E8DDD73A75903A700148A82C1D33799F333E249BF9E" }, 6 + { name = "envoy", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "95FD059345AA982E89A0B6E2A3BF1CF43E17A7048DCD85B5B65D3B9E4E39D359" }, 7 + { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, 8 + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, 9 + { name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" }, 10 + { name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" }, 11 + { name = "gleam_http", version = "4.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "82EA6A717C842456188C190AFB372665EA56CE13D8559BF3B1DD9E40F619EE0C" }, 12 + { name = "gleam_httpc", version = "5.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "C545172618D07811494E97AAA4A0FB34DA6F6D0061FDC8041C2F8E3BE2B2E48F" }, 13 + { name = "gleam_json", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "874FA3C3BB6E22DD2BB111966BD40B3759E9094E05257899A7C08F5DE77EC049" }, 14 + { name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" }, 15 + { name = "gleam_stdlib", version = "0.65.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "7C69C71D8C493AE11A5184828A77110EB05A7786EBF8B25B36A72F879C3EE107" }, 16 + { name = "gleam_time", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "DCDDC040CE97DA3D2A925CDBBA08D8A78681139745754A83998641C8A3F6587E" }, 17 + { name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" }, 18 + { name = "gleeunit", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "FDC68A8C492B1E9B429249062CD9BAC9B5538C6FBF584817205D0998C42E1DAC" }, 19 + { name = "glisten", version = "8.0.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "534BB27C71FB9E506345A767C0D76B17A9E9199934340C975DC003C710E3692D" }, 20 + { name = "gramps", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "8B7195978FBFD30B43DF791A8A272041B81E45D245314D7A41FC57237AA882A0" }, 21 + { name = "houdini", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "houdini", source = "hex", outer_checksum = "5DB1053F1AF828049C2B206D4403C18970ABEF5C18671CA3C2D2ED0DD64F6385" }, 22 + { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, 23 + { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, 24 + { name = "marceau", version = "1.3.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "2D1C27504BEF45005F5DFB18591F8610FB4BFA91744878210BDC464412EC44E9" }, 25 + { name = "mist", version = "5.0.3", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "gleam_yielder", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "7C4BE717A81305323C47C8A591E6B9BA4AC7F56354BF70B4D3DF08CC01192668" }, 26 + { name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" }, 27 + { name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" }, 28 + { name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" }, 29 + { name = "wisp", version = "2.1.0", build_tools = ["gleam"], requirements = ["directories", "exception", "filepath", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_json", "gleam_stdlib", "houdini", "logging", "marceau", "mist", "simplifile"], otp_app = "wisp", source = "hex", outer_checksum = "362BDDD11BF48EB38CDE51A73BC7D1B89581B395CA998E3F23F11EC026151C54" }, 30 + ] 31 + 32 + [requirements] 33 + gleam_httpc = { version = ">= 5.0.0 and < 6.0.0" } 34 + gleam_json = { version = ">= 3.0.2 and < 4.0.0" } 35 + gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 36 + gleam_time = { version = ">= 1.4.0 and < 2.0.0" } 37 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 38 + mist = { version = ">= 5.0.3 and < 6.0.0" } 39 + wisp = { version = ">= 2.1.0 and < 3.0.0" }
+111
src/diffusion.gleam
··· 1 + import gleam/io 2 + import gleam/json 3 + import gleam/time/duration 4 + import gleam/time/timestamp 5 + 6 + // An ActivityPub actor 7 + // type Actor { 8 + // Actor( 9 + // id: String, 10 + // preferred_username: String, 11 + // inbox: String, 12 + // outbox: String, 13 + // followers: String, 14 + // following: String, 15 + // public_key_id: String, 16 + // public_key_owner: String, 17 + // public_key_pem: String, 18 + // ) 19 + // } 20 + 21 + pub type User { 22 + User(internal_id: Int, username: String, public_key: String) 23 + } 24 + 25 + pub fn user_to_actor_json(domain: String, user: User) -> json.Json { 26 + let User(username:, public_key:, ..) = user 27 + 28 + json.object([ 29 + #("id", json.string(activitypub_url(domain, username, ""))), 30 + #("type", json.string("Person")), 31 + #("preferredUsername", json.string(username)), 32 + #("inbox", json.string(activitypub_url(domain, username, "/inbox"))), 33 + #("outbox", json.string(activitypub_url(domain, username, "/outbox"))), 34 + #("followers", json.string(activitypub_url(domain, username, "/followers"))), 35 + #("following", json.string(activitypub_url(domain, username, "/following"))), 36 + #( 37 + "publicKey", 38 + json.object([ 39 + #("id", json.string(activitypub_url(domain, username, "#main-key"))), 40 + #("owner", json.string(activitypub_url(domain, username, ""))), 41 + #("publicKeyPem", json.string(public_key)), 42 + ]), 43 + ), 44 + ]) 45 + } 46 + 47 + type Activity(a) { 48 + Activity( 49 + id: String, 50 + type_: String, 51 + actor: String, 52 + published: String, 53 + to: List(String), 54 + cc: List(String), 55 + object: a, 56 + ) 57 + } 58 + 59 + fn activity_to_json( 60 + domain: String, 61 + activity: Activity(a), 62 + object_encoder: fn(a) -> json.Json, 63 + ) -> json.Json { 64 + let Activity(id:, type_:, actor:, published:, to:, cc:, object:) = activity 65 + json.object([ 66 + #("id", json.string(id)), 67 + #("type_", json.string(type_)), 68 + #("actor", json.string(actor)), 69 + #("published", json.string(published)), 70 + #("to", json.array(to, json.string)), 71 + #("cc", json.array(cc, json.string)), 72 + #("object", object_encoder(object)), 73 + ]) 74 + } 75 + 76 + type Post { 77 + Post( 78 + id: String, 79 + content: String, 80 + published: timestamp.Timestamp, 81 + attributed_to: String, 82 + ) 83 + } 84 + 85 + fn post_to_json(domain: String, post: Post) -> json.Json { 86 + let Post(id:, content:, published:, attributed_to:) = post 87 + json.object([ 88 + #("@context", json.string("https://www.w3.org/ns/activitystreams")), 89 + #("id", json.string("https://" <> domain <> "/posts/" <> id)), 90 + #("type", json.string("Note")), 91 + #("content", json.string(content)), 92 + #( 93 + "published", 94 + json.string(published |> timestamp.to_rfc3339(duration.seconds(0))), 95 + ), 96 + #("attributedTo", json.string(attributed_to)), 97 + #( 98 + "to", 99 + json.array(["https://www.w3.org/ns/activitystreams#Public"], json.string), 100 + ), 101 + #("cc", json.array([attributed_to <> "/followers"], json.string)), 102 + ]) 103 + } 104 + 105 + pub fn main() -> Nil { 106 + io.println("Hello from diffusion!") 107 + } 108 + 109 + fn activitypub_url(domain: String, username: String, resource: String) { 110 + "https://" <> domain <> "/users/" <> username <> resource 111 + }
+17
test/diffusion_test.gleam
··· 1 + import diffusion 2 + import gleam/json 3 + import gleeunit 4 + 5 + const user = diffusion.User(1, "isaac", "abcd") 6 + 7 + pub fn main() -> Nil { 8 + gleeunit.main() 9 + } 10 + 11 + // gleeunit test functions end in `_test` 12 + pub fn encode_actor_test() { 13 + diffusion.user_to_actor_json("gleam.social", user) 14 + |> echo 15 + |> json.to_string 16 + |> echo 17 + }