Page MenuHomePhorge

No OneTemporary

Size
23 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
new file mode 100644
index 000000000..a894ac9c1
--- /dev/null
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -0,0 +1,13 @@
+defmodule Pleroma.Web.CommonAPI do
+ alias Pleroma.{Repo, Activity, Object}
+ alias Pleroma.Web.ActivityPub.ActivityPub
+
+ def delete(activity_id, user) do
+ with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id),
+ %Object{} = object <- Object.get_by_ap_id(object_id),
+ true <- user.ap_id == object.data["actor"],
+ {:ok, delete} <- ActivityPub.delete(object) do
+ {:ok, delete}
+ end
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 3a568cf2b..af62c3df0 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -1,71 +1,83 @@
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
use Pleroma.Web, :controller
alias Pleroma.{Repo, Activity}
alias Pleroma.Web.OAuth.App
alias Pleroma.Web
alias Pleroma.Web.MastodonAPI.{StatusView, AccountView}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.TwitterAPI.TwitterAPI
+ alias Pleroma.Web.CommonAPI
def create_app(conn, params) do
with cs <- App.register_changeset(%App{}, params) |> IO.inspect,
{:ok, app} <- Repo.insert(cs) |> IO.inspect do
res = %{
id: app.id,
client_id: app.client_id,
client_secret: app.client_secret
}
json(conn, res)
end
end
def verify_credentials(%{assigns: %{user: user}} = conn, params) do
account = AccountView.render("account.json", %{user: user})
json(conn, account)
end
def masto_instance(conn, _params) do
response = %{
uri: Web.base_url,
title: Web.base_url,
description: "A Pleroma instance, an alternative fediverse server",
version: "Pleroma Dev"
}
json(conn, response)
end
def home_timeline(%{assigns: %{user: user}} = conn, params) do
activities = ActivityPub.fetch_activities([user.ap_id | user.following], Map.put(params, "type", "Create"))
render conn, StatusView, "index.json", %{activities: activities, for: user, as: :activity}
end
def public_timeline(%{assigns: %{user: user}} = conn, params) do
params = params
|> Map.put("type", "Create")
|> Map.put("local_only", !!params["local"])
activities = ActivityPub.fetch_public_activities(params)
render conn, StatusView, "index.json", %{activities: activities, for: user, as: :activity}
end
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id) do
render conn, StatusView, "status.json", %{activity: activity, for: user}
end
end
def post_status(%{assigns: %{user: user}} = conn, %{"status" => status} = params) do
l = status |> String.trim |> String.length
params = params
|> Map.put("in_reply_to_status_id", params["in_reply_to_id"])
if l > 0 && l < 5000 do
{:ok, activity} = TwitterAPI.create_status(user, params)
render conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}
end
end
+
+ def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
+ json(conn, %{})
+ else
+ _e ->
+ conn
+ |> put_status(403)
+ |> json(%{error: "Can't delete this post"})
+ end
+ end
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 46cbf4e4e..d3cae6201 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -1,163 +1,164 @@
defmodule Pleroma.Web.Router do
use Pleroma.Web, :router
alias Pleroma.{Repo, User, Web.Router}
def user_fetcher(username) do
{:ok, Repo.get_by(User, %{nickname: username})}
end
pipeline :api do
plug :accepts, ["json"]
plug :fetch_session
plug Pleroma.Plugs.OAuthPlug
plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true}
end
pipeline :authenticated_api do
plug :accepts, ["json"]
plug :fetch_session
plug Pleroma.Plugs.OAuthPlug
plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1}
end
pipeline :well_known do
plug :accepts, ["xml", "xrd+xml"]
end
pipeline :config do
plug :accepts, ["json", "xml"]
end
pipeline :oauth do
plug :accepts, ["html", "json"]
end
scope "/oauth", Pleroma.Web.OAuth do
get "/authorize", OAuthController, :authorize
post "/authorize", OAuthController, :create_authorization
post "/token", OAuthController, :token_exchange
end
scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through :api
get "/instance", MastodonAPIController, :masto_instance
post "/apps", MastodonAPIController, :create_app
get "/timelines/public", MastodonAPIController, :public_timeline
get "/statuses/:id", MastodonAPIController, :get_status
end
scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through :authenticated_api
get "/accounts/verify_credentials", MastodonAPIController, :verify_credentials
get "/timelines/home", MastodonAPIController, :home_timeline
post "/statuses", MastodonAPIController, :post_status
+ delete "/statuses/:id", MastodonAPIController, :delete_status
end
scope "/api", Pleroma.Web do
pipe_through :config
get "/help/test", TwitterAPI.UtilController, :help_test
post "/help/test", TwitterAPI.UtilController, :help_test
get "/statusnet/config", TwitterAPI.UtilController, :config
get "/statusnet/version", TwitterAPI.UtilController, :version
end
scope "/api", Pleroma.Web do
pipe_through :api
get "/statuses/public_timeline", TwitterAPI.Controller, :public_timeline
get "/statuses/public_and_external_timeline", TwitterAPI.Controller, :public_and_external_timeline
get "/statuses/networkpublic_timeline", TwitterAPI.Controller, :public_and_external_timeline
get "/statuses/user_timeline", TwitterAPI.Controller, :user_timeline
get "/qvitter/statuses/user_timeline", TwitterAPI.Controller, :user_timeline
get "/statuses/show/:id", TwitterAPI.Controller, :fetch_status
get "/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation
post "/account/register", TwitterAPI.Controller, :register
get "/externalprofile/show", TwitterAPI.Controller, :external_profile
end
scope "/api", Pleroma.Web do
pipe_through :authenticated_api
get "/account/verify_credentials", TwitterAPI.Controller, :verify_credentials
post "/account/verify_credentials", TwitterAPI.Controller, :verify_credentials
post "/account/update_profile", TwitterAPI.Controller, :update_profile
post "/account/update_profile_banner", TwitterAPI.Controller, :update_banner
post "/qvitter/update_background_image", TwitterAPI.Controller, :update_background
post "/account/most_recent_notification", TwitterAPI.Controller, :update_most_recent_notification
get "/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline
get "/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline
get "/statuses/mentions", TwitterAPI.Controller, :mentions_timeline
get "/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline
post "/statuses/update", TwitterAPI.Controller, :status_update
post "/statuses/retweet/:id", TwitterAPI.Controller, :retweet
post "/statuses/destroy/:id", TwitterAPI.Controller, :delete_post
post "/friendships/create", TwitterAPI.Controller, :follow
post "/friendships/destroy", TwitterAPI.Controller, :unfollow
post "/statusnet/media/upload", TwitterAPI.Controller, :upload
post "/media/upload", TwitterAPI.Controller, :upload_json
post "/favorites/create/:id", TwitterAPI.Controller, :favorite
post "/favorites/create", TwitterAPI.Controller, :favorite
post "/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite
post "/qvitter/update_avatar", TwitterAPI.Controller, :update_avatar
get "/statuses/followers", TwitterAPI.Controller, :followers
get "/statuses/friends", TwitterAPI.Controller, :friends
end
pipeline :ostatus do
plug :accepts, ["xml", "atom", "html"]
end
scope "/", Pleroma.Web do
pipe_through :ostatus
get "/objects/:uuid", OStatus.OStatusController, :object
get "/activities/:uuid", OStatus.OStatusController, :activity
get "/users/:nickname/feed", OStatus.OStatusController, :feed
get "/users/:nickname", OStatus.OStatusController, :feed_redirect
post "/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming
post "/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request
get "/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation
post "/push/subscriptions/:id", Websub.WebsubController, :websub_incoming
end
scope "/.well-known", Pleroma.Web do
pipe_through :well_known
get "/host-meta", WebFinger.WebFingerController, :host_meta
get "/webfinger", WebFinger.WebFingerController, :webfinger
end
scope "/", Fallback do
get "/*path", RedirectController, :redirector
end
end
defmodule Fallback.RedirectController do
use Pleroma.Web, :controller
def redirector(conn, _params) do
if Mix.env != :test do
conn
|> put_resp_content_type("text/html")
|> send_file(200, "priv/static/index.html")
end
end
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 3ec54616a..5e0b9ea0a 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -1,302 +1,300 @@
defmodule Pleroma.Web.TwitterAPI.Controller do
use Pleroma.Web, :controller
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView}
alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
+ alias Pleroma.Web.CommonAPI
alias Pleroma.{Repo, Activity, User, Object}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Ecto.Changeset
require Logger
def verify_credentials(%{assigns: %{user: user}} = conn, _params) do
render(conn, UserView, "show.json", %{user: user})
end
def status_update(%{assigns: %{user: user}} = conn, %{"status" => status_text} = status_data) do
l = status_text |> String.trim |> String.length
if l > 0 && l < 5000 do
media_ids = extract_media_ids(status_data)
{:ok, activity} = TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids))
conn
|> json_reply(200, ActivityRepresenter.to_json(activity, %{user: user}))
else
empty_status_reply(conn)
end
end
def status_update(conn, _status_data) do
empty_status_reply(conn)
end
defp empty_status_reply(conn) do
bad_request_reply(conn, "Client must provide a 'status' parameter with a value.")
end
defp extract_media_ids(status_data) do
with media_ids when not is_nil(media_ids) <- status_data["media_ids"],
split_ids <- String.split(media_ids, ","),
clean_ids <- Enum.reject(split_ids, fn (id) -> String.length(id) == 0 end)
do
clean_ids
else _e -> []
end
end
def public_and_external_timeline(%{assigns: %{user: user}} = conn, params) do
statuses = TwitterAPI.fetch_public_and_external_statuses(user, params)
{:ok, json} = Poison.encode(statuses)
conn
|> json_reply(200, json)
end
def public_timeline(%{assigns: %{user: user}} = conn, params) do
statuses = TwitterAPI.fetch_public_statuses(user, params)
{:ok, json} = Poison.encode(statuses)
conn
|> json_reply(200, json)
end
def friends_timeline(%{assigns: %{user: user}} = conn, params) do
statuses = TwitterAPI.fetch_friend_statuses(user, params)
{:ok, json} = Poison.encode(statuses)
conn
|> json_reply(200, json)
end
def user_timeline(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.get_user(user, params) do
{:ok, target_user} ->
params = Map.merge(params, %{"actor_id" => target_user.ap_id})
statuses = TwitterAPI.fetch_user_statuses(user, params)
conn
|> json_reply(200, statuses |> Poison.encode!)
{:error, msg} ->
bad_request_reply(conn, msg)
end
end
def mentions_timeline(%{assigns: %{user: user}} = conn, params) do
statuses = TwitterAPI.fetch_mentions(user, params)
{:ok, json} = Poison.encode(statuses)
conn
|> json_reply(200, json)
end
def follow(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.follow(user, params) do
{:ok, user, followed, _activity} ->
render(conn, UserView, "show.json", %{user: followed, for: user})
{:error, msg} -> forbidden_json_reply(conn, msg)
end
end
def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, id),
- %Object{} = object <- Object.get_by_ap_id(object_id),
- true <- user.ap_id == object.data["actor"],
- {:ok, delete} <- ActivityPub.delete(object) |> IO.inspect do
+ with {:ok, delete} <- CommonAPI.delete(id, user) do
json = ActivityRepresenter.to_json(delete, %{user: user, for: user})
conn
|> json_reply(200, json)
end
end
def unfollow(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.unfollow(user, params) do
{:ok, user, unfollowed} ->
render(conn, UserView, "show.json", %{user: unfollowed, for: user})
{:error, msg} -> forbidden_json_reply(conn, msg)
end
end
def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
response = Poison.encode!(TwitterAPI.fetch_status(user, id))
conn
|> json_reply(200, response)
end
def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
id = String.to_integer(id)
response = Poison.encode!(TwitterAPI.fetch_conversation(user, id))
conn
|> json_reply(200, response)
end
def upload(conn, %{"media" => media}) do
response = TwitterAPI.upload(media)
conn
|> put_resp_content_type("application/atom+xml")
|> send_resp(200, response)
end
def upload_json(conn, %{"media" => media}) do
response = TwitterAPI.upload(media, "json")
conn
|> json_reply(200, response)
end
def get_by_id_or_ap_id(id) do
activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id)
if activity.data["type"] == "Create" do
activity
else
Activity.get_create_activity_by_object_ap_id(activity.data["object"])
end
end
def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
activity = get_by_id_or_ap_id(id)
{:ok, status} = TwitterAPI.favorite(user, activity)
response = Poison.encode!(status)
conn
|> json_reply(200, response)
end
def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
activity = get_by_id_or_ap_id(id)
{:ok, status} = TwitterAPI.unfavorite(user, activity)
response = Poison.encode!(status)
conn
|> json_reply(200, response)
end
def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
activity = get_by_id_or_ap_id(id)
if activity.data["actor"] == user.ap_id do
bad_request_reply(conn, "You cannot repeat your own notice.")
else
{:ok, status} = TwitterAPI.retweet(user, activity)
response = Poison.encode!(status)
conn
|> json_reply(200, response)
end
end
def register(conn, params) do
with {:ok, user} <- TwitterAPI.register_user(params) do
render(conn, UserView, "show.json", %{user: user})
else
{:error, errors} ->
conn
|> json_reply(400, Poison.encode!(errors))
end
end
def update_avatar(%{assigns: %{user: user}} = conn, params) do
{:ok, object} = ActivityPub.upload(params)
change = Changeset.change(user, %{avatar: object.data})
{:ok, user} = Repo.update(change)
render(conn, UserView, "show.json", %{user: user, for: user})
end
def update_banner(%{assigns: %{user: user}} = conn, params) do
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}),
new_info <- Map.put(user.info, "banner", object.data),
change <- User.info_changeset(user, %{info: new_info}),
{:ok, user} <- Repo.update(change) do
%{"url" => [ %{ "href" => href } | t ]} = object.data
response = %{ url: href } |> Poison.encode!
conn
|> json_reply(200, response)
end
end
def update_background(%{assigns: %{user: user}} = conn, params) do
with {:ok, object} <- ActivityPub.upload(params),
new_info <- Map.put(user.info, "background", object.data),
change <- User.info_changeset(user, %{info: new_info}),
{:ok, user} <- Repo.update(change) do
%{"url" => [ %{ "href" => href } | t ]} = object.data
response = %{ url: href } |> Poison.encode!
conn
|> json_reply(200, response)
end
end
def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => uri}) do
with {:ok, user_map} <- TwitterAPI.get_external_profile(current_user, uri),
response <- Poison.encode!(user_map) do
conn
|> json_reply(200, response)
end
end
def update_most_recent_notification(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with id when is_number(id) <- String.to_integer(id),
info <- user.info,
mrn <- max(id, user.info["most_recent_notification"] || 0),
updated_info <- Map.put(info, "most_recent_notification", mrn),
changeset <- User.info_changeset(user, %{info: updated_info}),
{:ok, user} <- Repo.update(changeset) do
conn
|> json_reply(200, Poison.encode!(mrn))
else
_e -> bad_request_reply(conn, "Can't update.")
end
end
def followers(%{assigns: %{user: user}} = conn, _params) do
with {:ok, followers} <- User.get_followers(user) do
render(conn, UserView, "index.json", %{users: followers, for: user})
else
_e -> bad_request_reply(conn, "Can't get followers")
end
end
def friends(%{assigns: %{user: user}} = conn, _params) do
with {:ok, friends} <- User.get_friends(user) do
render(conn, UserView, "index.json", %{users: friends, for: user})
else
_e -> bad_request_reply(conn, "Can't get friends")
end
end
def update_profile(%{assigns: %{user: user}} = conn, params) do
params = if bio = params["description"] do
Map.put(params, "bio", bio)
else
params
end
with changeset <- User.update_changeset(user, params),
{:ok, user} <- Repo.update(changeset) do
render(conn, UserView, "user.json", %{user: user, for: user})
else
error ->
Logger.debug("Can't update user: #{inspect(error)}")
bad_request_reply(conn, "Can't update user")
end
end
defp bad_request_reply(conn, error_message) do
json = error_json(conn, error_message)
json_reply(conn, 400, json)
end
defp json_reply(conn, status, json) do
conn
|> put_resp_content_type("application/json")
|> send_resp(status, json)
end
defp forbidden_json_reply(conn, error_message) do
json = error_json(conn, error_message)
json_reply(conn, 403, json)
end
defp error_json(conn, error_message) do
%{"error" => error_message, "request" => conn.request_path} |> Poison.encode!
end
end
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index a3692c9a0..d781af675 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -1,96 +1,124 @@
defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.Web.TwitterAPI.TwitterAPI
alias Pleroma.{Repo, User, Activity}
alias Pleroma.Web.OStatus
import Pleroma.Factory
test "the home timeline", %{conn: conn} do
user = insert(:user)
following = insert(:user)
{:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
conn = conn
|> assign(:user, user)
|> get("/api/v1/timelines/home")
assert length(json_response(conn, 200)) == 0
{:ok, user} = User.follow(user, following)
conn = build_conn()
|> assign(:user, user)
|> get("/api/v1/timelines/home")
assert [%{"content" => "test"}] = json_response(conn, 200)
end
test "the public timeline", %{conn: conn} do
following = insert(:user)
{:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
{:ok, [_activity]} = OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
conn = conn
|> get("/api/v1/timelines/public")
assert length(json_response(conn, 200)) == 2
conn = build_conn()
|> get("/api/v1/timelines/public", %{"local" => "True"})
assert [%{"content" => "test"}] = json_response(conn, 200)
end
test "posting a status", %{conn: conn} do
user = insert(:user)
conn = conn
|> assign(:user, user)
|> post("/api/v1/statuses", %{"status" => "cofe"})
assert %{"content" => "cofe", "id" => id} = json_response(conn, 200)
assert Repo.get(Activity, id)
end
test "replying to a status", %{conn: conn} do
user = insert(:user)
{:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
conn = conn
|> assign(:user, user)
|> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
activity = Repo.get(Activity, id)
assert activity.data["context"] == replied_to.data["context"]
assert activity.data["object"]["inReplyToStatusId"] == replied_to.id
end
test "verify_credentials", %{conn: conn} do
user = insert(:user)
conn = conn
|> assign(:user, user)
|> get("/api/v1/accounts/verify_credentials")
assert %{"id" => id} = json_response(conn, 200)
assert id == user.id
end
test "get a status", %{conn: conn} do
activity = insert(:note_activity)
conn = conn
|> get("/api/v1/statuses/#{activity.id}")
assert %{"id" => id} = json_response(conn, 200)
assert id == activity.id
end
+
+ describe "deleting a status" do
+ test "when you created it", %{conn: conn} do
+ activity = insert(:note_activity)
+ author = User.get_by_ap_id(activity.data["actor"])
+
+ conn = conn
+ |> assign(:user, author)
+ |> delete("/api/v1/statuses/#{activity.id}")
+
+ assert %{} = json_response(conn, 200)
+
+ assert Repo.get(Activity, activity.id) == nil
+ end
+
+ test "when you didn't create it", %{conn: conn} do
+ activity = insert(:note_activity)
+ user = insert(:user)
+
+ conn = conn
+ |> assign(:user, user)
+ |> delete("/api/v1/statuses/#{activity.id}")
+
+ assert %{"error" => _} = json_response(conn, 403)
+
+ assert Repo.get(Activity, activity.id) == activity
+ end
+ end
end

File Metadata

Mime Type
text/x-diff
Expires
Sun, Aug 9, 3:52 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1724176
Default Alt Text
(23 KB)

Event Timeline