Compare commits
2 commits
ac462cfe14
...
34de8792c0
| Author | SHA1 | Date | |
|---|---|---|---|
| 34de8792c0 | |||
| 90a243845e |
11 changed files with 244 additions and 88 deletions
58
README.md
58
README.md
|
|
@ -1,4 +1,4 @@
|
|||
## Posting
|
||||
# Features
|
||||
|
||||
- [x] Making posts
|
||||
- [x] Broadcasting them to followers
|
||||
|
|
@ -6,50 +6,55 @@
|
|||
- [x] Deleting posts
|
||||
- [x] Sending posts w/ images
|
||||
- [x] Making posts with CWs
|
||||
- [ ] Making polls
|
||||
- [ ] Followers-only posts (or maybe this is handled because we only send posts to followers? but we also include public in the TO field?)
|
||||
- [] Sending posts with videos
|
||||
|
||||
## Profile
|
||||
|
||||
- [x] Profile
|
||||
- [x] Name field (for display name)
|
||||
- [x] Bust actor cache when you update your profile
|
||||
|
||||
## Following
|
||||
|
||||
- [x] Sending follow request
|
||||
- [x] View following list
|
||||
- [x] Withdrawing follow request
|
||||
- [x] Unfollowing
|
||||
- [ ] Proactively check the outbox of newly-accepted follows
|
||||
|
||||
## Being Followed
|
||||
|
||||
- [x] Receiving follower requests
|
||||
- [ ] Viewing follower requests
|
||||
- [ ] Accepting follower requests
|
||||
- [ ] Rejecting follower requests
|
||||
- [ ] Ignoring follower requests
|
||||
- [ ] Unaccepting follower request ("soft block")
|
||||
- [ ] Blocking
|
||||
|
||||
## Timeline
|
||||
|
||||
- [x] Viewing follower requests
|
||||
- [x] Accepting follower requests
|
||||
- [x] Your posts show up in timeline
|
||||
- [x] Posts from accounts you follow show up in timeline
|
||||
- [x] Show the actor avatar and display name
|
||||
- [x] Receiving posts w/ images
|
||||
- [ ] Receiving posts w/ videos
|
||||
- [x] Liking posts
|
||||
- [x] Unliking posts
|
||||
- [x] Displaying CW posts behind CW
|
||||
- [x] Check that signature header (digest) matches digest of body contents
|
||||
- [x] Check the domain of the public key against the domain of the object being CRUDed
|
||||
- [x] Rejecting follower requests
|
||||
- [x] Ignoring follower requests
|
||||
- [x] Unaccepting follower request ("soft block")
|
||||
|
||||
# Roadmap
|
||||
|
||||
## Posting
|
||||
|
||||
- [ ] Making polls
|
||||
- [ ] Followers-only posts (or maybe this is handled because we only send posts to followers? but we also include public in the TO field?)
|
||||
- [ ] Sending posts with videos
|
||||
|
||||
## Following
|
||||
|
||||
- [ ] Proactively check the outbox of newly-accepted follows
|
||||
|
||||
## Being Followed
|
||||
|
||||
- [ ] Blocking
|
||||
|
||||
## Timeline
|
||||
|
||||
- [ ] Receiving posts w/ videos
|
||||
- [ ] Displaying polls
|
||||
- [ ] Voting in polls
|
||||
|
||||
## Individual Post Page
|
||||
|
||||
- [ ] Show post
|
||||
- [ ] Reply to post
|
||||
- [ ] Show replies to post (chronological order)
|
||||
|
||||
## DMs
|
||||
|
|
@ -70,11 +75,6 @@
|
|||
- [ ] Only accept activities from approved instances
|
||||
- [ ] Allow approved instances to see posts in outbox
|
||||
|
||||
## Protocol Support
|
||||
|
||||
- [x] Check that signature header (digest) matches digest of body contents
|
||||
- [x] Check the domain of the public key against the domain of the object being CRUDed
|
||||
|
||||
## Testing
|
||||
|
||||
- [ ] Measure test coverage
|
||||
|
|
|
|||
|
|
@ -18,34 +18,53 @@ defmodule ActivityPub do
|
|||
end
|
||||
|
||||
def post_activity(sender_id, private_key, inbox, activity) do
|
||||
body = Jason.encode!(activity)
|
||||
if String.contains?(inbox, "example.com") do
|
||||
{:ok, %{status: 200, body: "ok"}}
|
||||
else
|
||||
body = Jason.encode!(activity)
|
||||
|
||||
headers =
|
||||
Headers.signing_headers(
|
||||
"POST",
|
||||
inbox,
|
||||
body,
|
||||
sender_id,
|
||||
private_key
|
||||
)
|
||||
headers =
|
||||
Headers.signing_headers(
|
||||
"POST",
|
||||
inbox,
|
||||
body,
|
||||
sender_id,
|
||||
private_key
|
||||
)
|
||||
|
||||
Req.post(inbox, headers: headers, body: body)
|
||||
Req.post(inbox, headers: headers, body: body)
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_actor(actor_id) do
|
||||
request =
|
||||
Req.new(url: actor_id)
|
||||
|> Req.Request.put_header("accept", "application/json")
|
||||
if String.contains?(actor_id, "example.com") do
|
||||
{:ok,
|
||||
%{
|
||||
"@context" => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1"
|
||||
],
|
||||
"id" => actor_id,
|
||||
"type" => "Person",
|
||||
"preferredUsername" => "example",
|
||||
"name" => "Example User",
|
||||
"inbox" => "#{actor_id}/inbox"
|
||||
}}
|
||||
else
|
||||
request =
|
||||
Req.new(url: actor_id)
|
||||
|> Req.Request.put_header("accept", "application/json")
|
||||
|
||||
case Req.get(request) do
|
||||
{:ok, %{status: 200} = result} ->
|
||||
{:ok, result.body}
|
||||
case Req.get(request) do
|
||||
{:ok, %{status: 200} = result} ->
|
||||
{:ok, result.body}
|
||||
|
||||
{:ok, %{status: 404}} ->
|
||||
nil
|
||||
{:ok, %{status: 404}} ->
|
||||
nil
|
||||
|
||||
error ->
|
||||
error
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ defmodule Postland.Activities do
|
|||
def record_activity(params) do
|
||||
params
|
||||
|> Activity.changeset()
|
||||
|> Repo.insert()
|
||||
|> Repo.insert(on_conflict: :replace_all, conflict_target: :id)
|
||||
end
|
||||
|
||||
# TODO: add effects for CRUD notes
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ defmodule Postland.Follows do
|
|||
|> Repo.transaction()
|
||||
end
|
||||
|
||||
def record_and_reject(follow) do
|
||||
Multi.new()
|
||||
|> Multi.delete(:delete, follow)
|
||||
|> Multi.run(:send_reject_follow, fn _data, _repo ->
|
||||
send_reject(follow)
|
||||
end)
|
||||
|> Repo.transaction()
|
||||
end
|
||||
|
||||
def send_withdrawl(request) do
|
||||
request.followee
|
||||
|> ActivityPub.fetch_actor()
|
||||
|
|
@ -115,7 +124,7 @@ defmodule Postland.Follows do
|
|||
{:error, "could not find Follow activity to Accept"}
|
||||
|
||||
follow_activity ->
|
||||
body =
|
||||
activity =
|
||||
%{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Accept",
|
||||
|
|
@ -127,18 +136,51 @@ defmodule Postland.Follows do
|
|||
"type" => "Follow"
|
||||
}
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|
||||
headers =
|
||||
Headers.signing_headers(
|
||||
"POST",
|
||||
inbox,
|
||||
body,
|
||||
Postland.my_actor_id(),
|
||||
Accounts.solo_user().private_key
|
||||
)
|
||||
ActivityPub.post_activity(
|
||||
Postland.my_actor_id(),
|
||||
Accounts.solo_user().private_key,
|
||||
inbox,
|
||||
activity
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
Req.post(inbox, headers: headers, body: body)
|
||||
def send_reject(request) do
|
||||
request.follower
|
||||
|> ActivityPub.fetch_actor()
|
||||
|> do_send_reject(request)
|
||||
end
|
||||
|
||||
defp do_send_reject(nil, _), do: {:ok, nil}
|
||||
|
||||
defp do_send_reject({:ok, actor}, request) do
|
||||
inbox = Map.get(actor, "inbox")
|
||||
|
||||
case get_follow_activity(request.follower) do
|
||||
nil ->
|
||||
{:error, "could not find Follow activity to Reject"}
|
||||
|
||||
follow_activity ->
|
||||
activity =
|
||||
%{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Reject",
|
||||
"actor" => Postland.my_actor_id(),
|
||||
"object" => %{
|
||||
"actor" => request.follower,
|
||||
"id" => Map.get(follow_activity.data, "id"),
|
||||
"object" => Postland.my_actor_id(),
|
||||
"type" => "Follow"
|
||||
}
|
||||
}
|
||||
|
||||
ActivityPub.post_activity(
|
||||
Postland.my_actor_id(),
|
||||
Accounts.solo_user().private_key,
|
||||
inbox,
|
||||
activity
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -52,13 +52,29 @@ defmodule PostlandWeb.CoreComponents do
|
|||
</p>
|
||||
</div>
|
||||
<div class="flex-grow text-right">
|
||||
<.button
|
||||
:if={@status in [:follower_pending, :follower_confirmed]}
|
||||
phx-click="ignore"
|
||||
phx-value-dom-id={@dom_id}
|
||||
phx-value-id={@account["id"]}
|
||||
>
|
||||
{if @status == :follower_pending, do: "Ignore", else: "Soft Block"}
|
||||
</.button>
|
||||
<.button
|
||||
:if={@status == :follower_pending}
|
||||
phx-click="reject"
|
||||
phx-value-dom-id={@dom_id}
|
||||
phx-value-id={@account["id"]}
|
||||
>
|
||||
Reject
|
||||
</.button>
|
||||
<.button
|
||||
:if={@status == :follower_pending}
|
||||
phx-click="confirm"
|
||||
phx-value-dom-id={@dom_id}
|
||||
phx-value-id={@account["id"]}
|
||||
>
|
||||
Accept Request
|
||||
Accept
|
||||
</.button>
|
||||
<.button
|
||||
:if={@status == :following_pending}
|
||||
|
|
@ -484,7 +500,7 @@ defmodule PostlandWeb.CoreComponents do
|
|||
def simple_form(assigns) do
|
||||
~H"""
|
||||
<.form :let={f} for={@for} as={@as} {@rest}>
|
||||
<div class="mt-10 space-y-8 bg-white">
|
||||
<div class="space-y-8 bg-white">
|
||||
<%= render_slot(@inner_block, f) %>
|
||||
<div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6">
|
||||
<%= render_slot(action, f) %>
|
||||
|
|
|
|||
28
lib/postland_web/live/dev_live.ex
Normal file
28
lib/postland_web/live/dev_live.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule PostlandWeb.DevLive do
|
||||
use PostlandWeb, :live_view
|
||||
use Phoenix.VerifiedRoutes, endpoint: PostlandWeb.Endpoint, router: PostlandWeb.Router
|
||||
|
||||
alias Postland.Activities
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<.button phx-click="make_follower_request">Make follower request</.button>
|
||||
"""
|
||||
end
|
||||
|
||||
def handle_event("make_follower_request", _, socket) do
|
||||
mock_actor = "https://example.com/actors/test"
|
||||
encoded_followee = Base.url_encode64(Postland.my_actor_id(), padding: false)
|
||||
encoded_follower = Base.url_encode64(mock_actor, padding: false)
|
||||
|
||||
Activities.process_activity(%{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => url(~p"/follows/#{encoded_followee}/#{encoded_follower}"),
|
||||
"type" => "Follow",
|
||||
"actor" => mock_actor,
|
||||
"object" => Postland.my_actor_id()
|
||||
})
|
||||
|
||||
{:noreply, socket |> push_navigate(to: ~p"/followers")}
|
||||
end
|
||||
end
|
||||
|
|
@ -43,17 +43,17 @@ defmodule PostlandWeb.FollowersLive do
|
|||
{:ok, socket |> stream(:accounts, followers) |> assign(count: account_count)}
|
||||
end
|
||||
|
||||
def handle_event("confirm", %{"id" => id, "dom-id" => dom_id}, socket) do
|
||||
def handle_event("confirm", %{"id" => id}, socket) do
|
||||
request = Follows.get(id, Postland.my_actor_id())
|
||||
|
||||
socket =
|
||||
case Follows.record_and_send_acceptance(request) do
|
||||
{:ok, _} ->
|
||||
request = Follows.get(Postland.my_actor_id(), id)
|
||||
request = Follows.get(id, Postland.my_actor_id())
|
||||
|
||||
acct = %{
|
||||
id: request.follower,
|
||||
confirmed: !!request.onfirmed_at,
|
||||
confirmed: !!request.confirmed_at,
|
||||
account: Actors.actor(request.follower)
|
||||
}
|
||||
|
||||
|
|
@ -66,4 +66,48 @@ defmodule PostlandWeb.FollowersLive do
|
|||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("reject", %{"id" => id}, socket) do
|
||||
request = Follows.get(id, Postland.my_actor_id())
|
||||
|
||||
socket =
|
||||
case Follows.record_and_reject(request) do
|
||||
{:ok, _} ->
|
||||
acct = %{
|
||||
id: request.follower,
|
||||
confirmed: true,
|
||||
account: Actors.actor(request.follower)
|
||||
}
|
||||
|
||||
socket
|
||||
|> stream_delete(:accounts, acct)
|
||||
|> assign(count: socket.assigns.count - 1)
|
||||
|
||||
_ ->
|
||||
put_flash(socket, :error, "An unexpected error occurred.")
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("ignore", %{"id" => id}, socket) do
|
||||
request = Follows.get(id, Postland.my_actor_id())
|
||||
|
||||
case Postland.Repo.delete(request) do
|
||||
{:ok, _} ->
|
||||
acct = %{
|
||||
id: request.follower,
|
||||
confirmed: true,
|
||||
account: Actors.actor(request.follower)
|
||||
}
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> stream_delete(:accounts, acct)
|
||||
|> assign(count: socket.assigns.count - 1)}
|
||||
|
||||
_ ->
|
||||
{:noreply, put_flash(socket, :error, "An unexpected error occurred.")}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,19 +8,21 @@ defmodule PostlandWeb.UserLoginLive do
|
|||
Log in to account
|
||||
</.header>
|
||||
|
||||
<.simple_form for={@form} id="login_form" action={~p"/users/log_in"} phx-update="ignore">
|
||||
<.input field={@form[:username]} type="text" label="Username" required />
|
||||
<.input field={@form[:password]} type="password" label="Password" required />
|
||||
<div class="p-4 mt-10 bg-white rounded">
|
||||
<.simple_form for={@form} id="login_form" action={~p"/users/log_in"} phx-update="ignore">
|
||||
<.input field={@form[:username]} type="text" label="Username" required />
|
||||
<.input field={@form[:password]} type="password" label="Password" required />
|
||||
|
||||
<:actions>
|
||||
<.input field={@form[:remember_me]} type="checkbox" label="Keep me logged in" />
|
||||
</:actions>
|
||||
<:actions>
|
||||
<.button phx-disable-with="Logging in..." class="w-full">
|
||||
Log in <span aria-hidden="true">→</span>
|
||||
</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
<:actions>
|
||||
<.input field={@form[:remember_me]} type="checkbox" label="Keep me logged in" />
|
||||
</:actions>
|
||||
<:actions>
|
||||
<.button phx-disable-with="Logging in..." class="w-full">
|
||||
Log in <span aria-hidden="true">→</span>
|
||||
</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
|
|
|||
|
|
@ -58,6 +58,11 @@ defmodule PostlandWeb.Router do
|
|||
pipe_through :browser
|
||||
|
||||
live_dashboard "/dashboard", metrics: PostlandWeb.Telemetry
|
||||
|
||||
live_session :mount_current_user_dev,
|
||||
on_mount: [{PostlandWeb.UserAuth, :mount_current_user}] do
|
||||
live "/mocks", PostlandWeb.DevLive
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
2
mix.exs
2
mix.exs
|
|
@ -39,7 +39,7 @@ defmodule Postland.MixProject do
|
|||
{:ecto_sqlite3, ">= 0.0.0"},
|
||||
{:phoenix_html, "~> 4.0"},
|
||||
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
||||
{:phoenix_live_view, "~> 0.20.2"},
|
||||
{:phoenix_live_view, "~> 1.0.0"},
|
||||
{:floki, ">= 0.30.0", only: :test},
|
||||
{:phoenix_live_dashboard, "~> 0.8.3"},
|
||||
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
|
||||
|
|
|
|||
18
mix.lock
18
mix.lock
|
|
@ -1,12 +1,12 @@
|
|||
%{
|
||||
"bandit": {:hex, :bandit, "1.6.0", "9cb6c67c27cecab2d0c93968cb957fa8decccb7275193c8bf33f97397b3ac25d", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "fd2491e564a7c5e11ff8496ebf530c342c742452c59de17ac0fb1f814a0ab01a"},
|
||||
"bandit": {:hex, :bandit, "1.6.1", "9e01b93d72ddc21d8c576a704949e86ee6cde7d11270a1d3073787876527a48f", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5a904bf010ea24b67979835e0507688e31ac873d4ffc8ed0e5413e8d77455031"},
|
||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"},
|
||||
"cachex": {:hex, :cachex, "4.0.2", "120f9c27b0a453c7cb3319d9dc6c61c050a480e5299fc1f8bded1e2e334992ab", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:ex_hash_ring, "~> 6.0", [hex: :ex_hash_ring, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "4f4890122bddd979f6c217d5e300d0c0d3eb858a976cbe1f65a94e6322bc5825"},
|
||||
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
|
||||
"castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"},
|
||||
"cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"},
|
||||
"comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"},
|
||||
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
|
||||
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
||||
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
|
||||
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
|
||||
"earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"},
|
||||
"ecto": {:hex, :ecto, "3.12.3", "1a9111560731f6c3606924c81c870a68a34c819f6d4f03822f370ea31a582208", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9efd91506ae722f95e48dc49e70d0cb632ede3b7a23896252a60a14ac6d59165"},
|
||||
|
|
@ -20,22 +20,22 @@
|
|||
"exqlite": {:hex, :exqlite, "0.24.1", "b15f6c7ca14e75b14637339bdd50d96ce6dc94ea4c6a133f94037d2379373f91", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "415e36e3f8d6ae9fb51327f481b0ce8858414abf838b565cf164469596762cb0"},
|
||||
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
|
||||
"finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"},
|
||||
"floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"},
|
||||
"floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"},
|
||||
"gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"},
|
||||
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized"]},
|
||||
"hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"},
|
||||
"hpax": {:hex, :hpax, "1.0.2", "762df951b0c399ff67cc57c3995ec3cf46d696e41f0bba17da0518d94acd4aac", [:mix], [], "hexpm", "2f09b4c1074e0abd846747329eaa26d535be0eb3d189fa69d812bfb8bfefd32f"},
|
||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
||||
"jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"},
|
||||
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
|
||||
"mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
|
||||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||
"phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"},
|
||||
"phoenix": {:hex, :phoenix, "1.7.18", "5310c21443514be44ed93c422e15870aef254cf1b3619e4f91538e7529d2b2e4", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "1797fcc82108442a66f2c77a643a62980f342bfeb63d6c9a515ab8294870004e"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.2", "3b83b24ab5a2eb071a20372f740d7118767c272db386831b2e77638c4dcc606d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "3f94d025f59de86be00f5f8c5dd7b5965a3298458d21ab1c328488be3b5fcd59"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
|
||||
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.4", "4508e481f791ce62ec6a096e13b061387158cbeefacca68c6c1928e1305e23ed", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "2984aae96994fbc5c61795a73b8fb58153b41ff934019cfb522343d2d3817d59"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.17", "f396bbdaf4ba227b82251eb75ac0afa6b3da5e509bc0d030206374237dfc9450", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a61d741ffb78c85fdbca0de084da6a48f8ceb5261a79165b5a0b59e5f65ce98b"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "1.0.1", "5389a30658176c0de816636ce276567478bffd063c082515a6e8368b8fc9a0db", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c0f517e6f290f10dbb94343ac22e0109437fb1fa6f0696e7c73967b789c1c285"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
|
||||
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
||||
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
|
||||
|
|
@ -48,8 +48,8 @@
|
|||
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
|
||||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.3.6", "835a626a8a6f6a1e681b63e1132a8427e87ce443aaf4888fbf63b2df77539b97", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0ed8798084c8c49a223840b20598b022e4eb8c9f390fb6701864c307fc9aa2cd"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.3.7", "1da7598c0f4f5f50562c097a3f8af308ded48cd35139f0e6f17d9443e4d0c9c5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0139335079953de41d381a6134d8b618d53d084f558c734f2662d1a72818dd12"},
|
||||
"unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"},
|
||||
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||
"websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"},
|
||||
"websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"},
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue