Add ability to set a default post expiry (#321)
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is pending
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is pending
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk> Reviewed-on: #321
This commit is contained in:
parent
2d019e14e3
commit
0cfd5b4e89
13 changed files with 107 additions and 11 deletions
|
@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
## Added
|
## Added
|
||||||
- Config: HTTP timeout options, :pool\_timeout and :receive\_timeout
|
- Config: HTTP timeout options, :pool\_timeout and :receive\_timeout
|
||||||
- Added statistic gathering about instances which do/don't have signed fetches when they request from us
|
- Added statistic gathering about instances which do/don't have signed fetches when they request from us
|
||||||
|
- Ability to set a default post expiry time, after which the post will be deleted. If used in concert with ActivityExpiration MRF, the expiry which comes _sooner_ will be applied.
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
- MastoAPI: Accept BooleanLike input on `/api/v1/accounts/:id/follow` (fixes follows with mastodon.py)
|
- MastoAPI: Accept BooleanLike input on `/api/v1/accounts/:id/follow` (fixes follows with mastodon.py)
|
||||||
|
|
|
@ -151,6 +151,7 @@ defmodule Pleroma.User do
|
||||||
field(:is_suggested, :boolean, default: false)
|
field(:is_suggested, :boolean, default: false)
|
||||||
field(:last_status_at, :naive_datetime)
|
field(:last_status_at, :naive_datetime)
|
||||||
field(:language, :string)
|
field(:language, :string)
|
||||||
|
field(:status_ttl_days, :integer, default: nil)
|
||||||
|
|
||||||
embeds_one(
|
embeds_one(
|
||||||
:notification_settings,
|
:notification_settings,
|
||||||
|
@ -516,7 +517,8 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
:pleroma_settings_store,
|
:pleroma_settings_store,
|
||||||
:is_discoverable,
|
:is_discoverable,
|
||||||
:actor_type,
|
:actor_type,
|
||||||
:disclose_client
|
:disclose_client,
|
||||||
|
:status_ttl_days
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|
@ -524,6 +526,7 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|> validate_length(:name, min: 1, max: name_limit)
|
|> validate_length(:name, min: 1, max: name_limit)
|
||||||
|> validate_inclusion(:actor_type, ["Person", "Service"])
|
|> validate_inclusion(:actor_type, ["Person", "Service"])
|
||||||
|
|> validate_number(:status_ttl_days, greater_than: 0)
|
||||||
|> put_fields()
|
|> put_fields()
|
||||||
|> put_emoji()
|
|> put_emoji()
|
||||||
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
||||||
|
|
|
@ -700,7 +700,13 @@ defp update_credentials_request do
|
||||||
description:
|
description:
|
||||||
"Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
|
"Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
|
||||||
},
|
},
|
||||||
actor_type: ActorType
|
actor_type: ActorType,
|
||||||
|
status_ttl_days: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
nullable: true,
|
||||||
|
description:
|
||||||
|
"Number of days after which statuses will be deleted. Set to -1 to disable."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
example: %{
|
example: %{
|
||||||
bot: false,
|
bot: false,
|
||||||
|
@ -720,7 +726,8 @@ defp update_credentials_request do
|
||||||
allow_following_move: false,
|
allow_following_move: false,
|
||||||
also_known_as: ["https://foo.bar/users/foo"],
|
also_known_as: ["https://foo.bar/users/foo"],
|
||||||
discoverable: false,
|
discoverable: false,
|
||||||
actor_type: "Person"
|
actor_type: "Person",
|
||||||
|
status_ttl_days: 30
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -109,6 +109,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
akkoma: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
note_ttl_days: %Schema{type: :integer}
|
||||||
|
}
|
||||||
|
},
|
||||||
source: %Schema{
|
source: %Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
|
|
|
@ -175,6 +175,11 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
value -> {:ok, value}
|
value -> {:ok, value}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
status_ttl_days_value = fn
|
||||||
|
-1 -> {:ok, nil}
|
||||||
|
value -> {:ok, value}
|
||||||
|
end
|
||||||
|
|
||||||
user_params =
|
user_params =
|
||||||
[
|
[
|
||||||
:no_rich_text,
|
:no_rich_text,
|
||||||
|
@ -215,7 +220,9 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
# Note: param name is indeed :discoverable (not an error)
|
# Note: param name is indeed :discoverable (not an error)
|
||||||
|> Maps.put_if_present(:is_discoverable, params[:discoverable])
|
|> Maps.put_if_present(:is_discoverable, params[:discoverable])
|
||||||
|> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language]))
|
|> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language]))
|
||||||
|
|> Maps.put_if_present(:status_ttl_days, params[:status_ttl_days], status_ttl_days_value)
|
||||||
|
|
||||||
|
IO.inspect(user_params)
|
||||||
# What happens here:
|
# What happens here:
|
||||||
#
|
#
|
||||||
# We want to update the user through the pipeline, but the ActivityPub
|
# We want to update the user through the pipeline, but the ActivityPub
|
||||||
|
|
|
@ -171,6 +171,16 @@ def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn,
|
||||||
Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id])
|
Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id])
|
||||||
|> put_application(conn)
|
|> put_application(conn)
|
||||||
|
|
||||||
|
expires_in_seconds =
|
||||||
|
if is_nil(user.status_ttl_days),
|
||||||
|
do: nil,
|
||||||
|
else: 60 * 60 * 24 * user.status_ttl_days
|
||||||
|
|
||||||
|
params =
|
||||||
|
if is_nil(expires_in_seconds),
|
||||||
|
do: params,
|
||||||
|
else: Map.put(params, :expires_in, expires_in_seconds)
|
||||||
|
|
||||||
with {:ok, activity} <- CommonAPI.post(user, params) do
|
with {:ok, activity} <- CommonAPI.post(user, params) do
|
||||||
try_render(conn, "show.json",
|
try_render(conn, "show.json",
|
||||||
activity: activity,
|
activity: activity,
|
||||||
|
|
|
@ -287,7 +287,8 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
},
|
},
|
||||||
last_status_at: user.last_status_at,
|
last_status_at: user.last_status_at,
|
||||||
akkoma: %{
|
akkoma: %{
|
||||||
instance: render("instance.json", %{instance: instance})
|
instance: render("instance.json", %{instance: instance}),
|
||||||
|
status_ttl_days: user.status_ttl_days
|
||||||
},
|
},
|
||||||
# Pleroma extensions
|
# Pleroma extensions
|
||||||
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
|
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
|
||||||
|
|
|
@ -65,7 +65,7 @@ defp get_replied_to_activities(activities) do
|
||||||
# This should be removed in a future version of Pleroma. Pleroma-FE currently
|
# This should be removed in a future version of Pleroma. Pleroma-FE currently
|
||||||
# depends on this field, as well.
|
# depends on this field, as well.
|
||||||
defp get_context_id(%{data: %{"context" => context}}) when is_binary(context) do
|
defp get_context_id(%{data: %{"context" => context}}) when is_binary(context) do
|
||||||
use Bitwise
|
import Bitwise
|
||||||
|
|
||||||
:erlang.crc32(context)
|
:erlang.crc32(context)
|
||||||
|> band(bnot(0x8000_0000))
|
|> band(bnot(0x8000_0000))
|
||||||
|
|
|
@ -14,14 +14,14 @@ def change do
|
||||||
from(u in User,
|
from(u in User,
|
||||||
where: u.local == true,
|
where: u.local == true,
|
||||||
where: is_nil(u.keys),
|
where: is_nil(u.keys),
|
||||||
select: u
|
select: u.id
|
||||||
)
|
)
|
||||||
|
|
||||||
Repo.stream(query)
|
Repo.stream(query)
|
||||||
|> Enum.each(fn user ->
|
|> Enum.each(fn user ->
|
||||||
with {:ok, pem} <- Keys.generate_rsa_pem() do
|
with {:ok, pem} <- Keys.generate_rsa_pem() do
|
||||||
Ecto.Changeset.cast(user, %{keys: pem}, [:keys])
|
Ecto.Changeset.cast(%User{id: user}, %{keys: pem}, [:keys])
|
||||||
|> Repo.update()
|
|> Repo.update(returning: false)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddPerUserPostExpiry do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:status_ttl_days, :integer, null: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -126,6 +126,35 @@ test "posting a status", %{conn: conn} do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "automatically setting a post expiry if status_ttl_days is set" do
|
||||||
|
user = insert(:user, status_ttl_days: 1)
|
||||||
|
%{user: _user, token: _token, conn: conn} = oauth_access(["write:statuses"], user: user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("api/v1/statuses", %{
|
||||||
|
"status" => "aa chikichiki banban"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
|
activity = Activity.get_by_id_with_object(id)
|
||||||
|
{:ok, expires_at, _} = DateTime.from_iso8601(activity.data["expires_at"])
|
||||||
|
|
||||||
|
assert Timex.diff(
|
||||||
|
expires_at,
|
||||||
|
DateTime.utc_now(),
|
||||||
|
:hours
|
||||||
|
) == 23
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: Pleroma.Workers.PurgeExpiredActivity,
|
||||||
|
args: %{activity_id: id},
|
||||||
|
scheduled_at: DateTime.add(DateTime.utc_now(), 1 * 60 * 60 * 24)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
test "it fails to create a status if `expires_in` is less or equal than an hour", %{
|
test "it fails to create a status if `expires_in` is less or equal than an hour", %{
|
||||||
conn: conn
|
conn: conn
|
||||||
} do
|
} do
|
||||||
|
|
|
@ -209,6 +209,26 @@ test "updates the user's name", %{conn: conn} do
|
||||||
assert update_activity.data["object"]["name"] == "markorepairs"
|
assert update_activity.data["object"]["name"] == "markorepairs"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "updates the user's default post expiry", %{conn: conn} do
|
||||||
|
conn = patch(conn, "/api/v1/accounts/update_credentials", %{"status_ttl_days" => "1"})
|
||||||
|
|
||||||
|
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||||
|
assert user_data["akkoma"]["status_ttl_days"] == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "resets the user's default post expiry", %{conn: conn} do
|
||||||
|
conn = patch(conn, "/api/v1/accounts/update_credentials", %{"status_ttl_days" => "-1"})
|
||||||
|
|
||||||
|
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||||
|
assert is_nil(user_data["akkoma"]["status_ttl_days"])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not allow negative integers other than -1 for TTL", %{conn: conn} do
|
||||||
|
conn = patch(conn, "/api/v1/accounts/update_credentials", %{"status_ttl_days" => "-2"})
|
||||||
|
|
||||||
|
assert user_data = json_response_and_validate_schema(conn, 403)
|
||||||
|
end
|
||||||
|
|
||||||
test "updates the user's AKAs", %{conn: conn} do
|
test "updates the user's AKAs", %{conn: conn} do
|
||||||
conn =
|
conn =
|
||||||
patch(conn, "/api/v1/accounts/update_credentials", %{
|
patch(conn, "/api/v1/accounts/update_credentials", %{
|
||||||
|
|
|
@ -37,7 +37,8 @@ test "Represent a user account" do
|
||||||
inserted_at: ~N[2017-08-15 15:47:06.597036],
|
inserted_at: ~N[2017-08-15 15:47:06.597036],
|
||||||
emoji: %{"karjalanpiirakka" => "/file.png"},
|
emoji: %{"karjalanpiirakka" => "/file.png"},
|
||||||
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"",
|
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"",
|
||||||
also_known_as: ["https://shitposter.zone/users/shp"]
|
also_known_as: ["https://shitposter.zone/users/shp"],
|
||||||
|
status_ttl_days: 5
|
||||||
})
|
})
|
||||||
|
|
||||||
insert(:instance, %{host: "example.com", nodeinfo: %{version: "2.1"}})
|
insert(:instance, %{host: "example.com", nodeinfo: %{version: "2.1"}})
|
||||||
|
@ -61,7 +62,8 @@ test "Represent a user account" do
|
||||||
"version" => "2.1"
|
"version" => "2.1"
|
||||||
},
|
},
|
||||||
favicon: nil
|
favicon: nil
|
||||||
}
|
},
|
||||||
|
status_ttl_days: 5
|
||||||
},
|
},
|
||||||
avatar: "http://localhost:4001/images/avi.png",
|
avatar: "http://localhost:4001/images/avi.png",
|
||||||
avatar_static: "http://localhost:4001/images/avi.png",
|
avatar_static: "http://localhost:4001/images/avi.png",
|
||||||
|
@ -243,7 +245,8 @@ test "Represent a Service(bot) account" do
|
||||||
name: "localhost",
|
name: "localhost",
|
||||||
favicon: "http://localhost:4001/favicon.png",
|
favicon: "http://localhost:4001/favicon.png",
|
||||||
nodeinfo: %{version: "2.0"}
|
nodeinfo: %{version: "2.0"}
|
||||||
}
|
},
|
||||||
|
status_ttl_days: nil
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
|
|
Loading…
Reference in a new issue