mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-01 16:51:21 +00:00
Adding listMedia endpoint, to view all your local image uploads.
- Fixes #4445
This commit is contained in:
parent
6778279bb6
commit
3c51eaeb88
10 changed files with 109 additions and 26 deletions
|
@ -27,7 +27,7 @@
|
|||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"jest": "^29.5.0",
|
||||
"lemmy-js-client": "0.19.4-alpha.6",
|
||||
"lemmy-js-client": "0.19.4-alpha.7",
|
||||
"prettier": "^3.2.5",
|
||||
"ts-jest": "^29.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
|
|
|
@ -30,8 +30,8 @@ devDependencies:
|
|||
specifier: ^29.5.0
|
||||
version: 29.7.0(@types/node@20.11.22)
|
||||
lemmy-js-client:
|
||||
specifier: 0.19.4-alpha.6
|
||||
version: 0.19.4-alpha.6
|
||||
specifier: 0.19.4-alpha.7
|
||||
version: 0.19.4-alpha.7
|
||||
prettier:
|
||||
specifier: ^3.2.5
|
||||
version: 3.2.5
|
||||
|
@ -2390,8 +2390,8 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/lemmy-js-client@0.19.4-alpha.6:
|
||||
resolution: {integrity: sha512-x4htMlpoZ7hzrhrIk82aompVxbpu2ZDWtmWNGraM0+27nUCDf6gYxJH5nb5R/o39BQe5KSHq6zoBdliBwAY40w==}
|
||||
/lemmy-js-client@0.19.4-alpha.7:
|
||||
resolution: {integrity: sha512-1xvSDlhJmU3IzhT2+pvqPWKHo0P/aYTlpObL3hLy1RgaZLapvn3W7XC48cOydas+MAm2WBFsiFX9bi5X+5FWFA==}
|
||||
dependencies:
|
||||
cross-fetch: 4.0.0
|
||||
form-data: 4.0.0
|
||||
|
|
|
@ -48,6 +48,15 @@ test("Upload image and delete it", async () => {
|
|||
const content = downloadFileSync(upload.url);
|
||||
expect(content.length).toBeGreaterThan(0);
|
||||
|
||||
// Ensure that it comes back with the list_media endpoint
|
||||
const listMediaRes = await alphaImage.listMedia({});
|
||||
expect(listMediaRes.images.length).toBe(1);
|
||||
|
||||
// The deleteUrl is a combination of the endpoint, delete token, and alias
|
||||
let firstImage = listMediaRes.images[0];
|
||||
let deleteUrl = `${alphaUrl}/pictrs/image/delete/${firstImage.pictrs_delete_token}/${firstImage.pictrs_alias}`;
|
||||
expect(deleteUrl).toBe(upload.delete_url);
|
||||
|
||||
// delete image
|
||||
const delete_form: DeleteImage = {
|
||||
token: upload.files![0].delete_token,
|
||||
|
|
28
crates/api/src/local_user/list_media.rs
Normal file
28
crates/api/src/local_user/list_media.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use actix_web::web::{Data, Json, Query};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::{ListMedia, ListMediaResponse},
|
||||
};
|
||||
use lemmy_db_schema::source::images::LocalImage;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
||||
/// Lists comment reports for a community if an id is supplied
|
||||
/// or returns all comment reports for communities a user moderates
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn list_media(
|
||||
data: Query<ListMedia>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> Result<Json<ListMediaResponse>, LemmyError> {
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
let images = LocalImage::get_all_paged_by_local_user_id(
|
||||
&mut context.pool(),
|
||||
local_user_view.local_user.id,
|
||||
page,
|
||||
limit,
|
||||
)
|
||||
.await?;
|
||||
Ok(Json(ListMediaResponse { images }))
|
||||
}
|
|
@ -10,6 +10,7 @@ pub mod generate_totp_secret;
|
|||
pub mod get_captcha;
|
||||
pub mod list_banned;
|
||||
pub mod list_logins;
|
||||
pub mod list_media;
|
||||
pub mod login;
|
||||
pub mod logout;
|
||||
pub mod notifications;
|
||||
|
|
|
@ -32,7 +32,7 @@ pub async fn purge_person(
|
|||
// Read the local user to get their images, and delete them
|
||||
if let Ok(local_user) = LocalUserView::read_person(&mut context.pool(), data.person_id).await {
|
||||
let pictrs_uploads =
|
||||
LocalImage::get_all_by_local_user_id(&mut context.pool(), &local_user.local_user.id).await?;
|
||||
LocalImage::get_all_by_local_user_id(&mut context.pool(), local_user.local_user.id).await?;
|
||||
|
||||
for upload in pictrs_uploads {
|
||||
delete_image_from_pictrs(&upload.pictrs_alias, &upload.pictrs_delete_token, &context)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sensitive::Sensitive;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommentReplyId, CommunityId, LanguageId, PersonId, PersonMentionId},
|
||||
source::site::Site,
|
||||
source::{images::LocalImage, site::Site},
|
||||
CommentSortType,
|
||||
ListingType,
|
||||
PostListingMode,
|
||||
|
@ -418,3 +418,20 @@ pub struct UpdateTotp {
|
|||
pub struct UpdateTotpResponse {
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Get your user's image / media uploads.
|
||||
pub struct ListMedia {
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct ListMediaResponse {
|
||||
pub images: Vec<LocalImage>,
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
use crate::{
|
||||
newtypes::{DbUrl, LocalUserId},
|
||||
schema::{
|
||||
local_image::dsl::{local_image, local_user_id, pictrs_alias},
|
||||
remote_image::dsl::{link, remote_image},
|
||||
},
|
||||
schema::{local_image, remote_image},
|
||||
source::images::{LocalImage, LocalImageForm, RemoteImage, RemoteImageForm},
|
||||
utils::{get_conn, DbPool},
|
||||
utils::{get_conn, limit_and_offset, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
dsl::exists,
|
||||
|
@ -15,7 +12,6 @@ use diesel::{
|
|||
ExpressionMethods,
|
||||
NotFound,
|
||||
QueryDsl,
|
||||
Table,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use url::Url;
|
||||
|
@ -23,27 +19,47 @@ use url::Url;
|
|||
impl LocalImage {
|
||||
pub async fn create(pool: &mut DbPool<'_>, form: &LocalImageForm) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(local_image)
|
||||
insert_into(local_image::table)
|
||||
.values(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
/// This should only be used in the internal API, since it has no page and limit
|
||||
pub async fn get_all_by_local_user_id(
|
||||
pool: &mut DbPool<'_>,
|
||||
user_id: &LocalUserId,
|
||||
user_id: LocalUserId,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
local_image
|
||||
.filter(local_user_id.eq(user_id))
|
||||
.select(local_image::all_columns())
|
||||
local_image::table
|
||||
.filter(local_image::local_user_id.eq(user_id))
|
||||
.select(local_image::all_columns)
|
||||
.load::<LocalImage>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
/// This is okay for API use.
|
||||
pub async fn get_all_paged_by_local_user_id(
|
||||
pool: &mut DbPool<'_>,
|
||||
user_id: LocalUserId,
|
||||
page: Option<i64>,
|
||||
limit: Option<i64>,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (limit, offset) = limit_and_offset(page, limit)?;
|
||||
|
||||
local_image::table
|
||||
.filter(local_image::local_user_id.eq(user_id))
|
||||
.select(local_image::all_columns)
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<LocalImage>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_by_alias(pool: &mut DbPool<'_>, alias: &str) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::delete(local_image.filter(pictrs_alias.eq(alias)))
|
||||
diesel::delete(local_image::table.filter(local_image::pictrs_alias.eq(alias)))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
|
@ -56,7 +72,7 @@ impl RemoteImage {
|
|||
.into_iter()
|
||||
.map(|url| RemoteImageForm { link: url.into() })
|
||||
.collect::<Vec<_>>();
|
||||
insert_into(remote_image)
|
||||
insert_into(remote_image::table)
|
||||
.values(forms)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(conn)
|
||||
|
@ -66,9 +82,11 @@ impl RemoteImage {
|
|||
pub async fn validate(pool: &mut DbPool<'_>, link_: DbUrl) -> Result<(), Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let exists = select(exists(remote_image.filter((link).eq(link_))))
|
||||
.get_result::<bool>(conn)
|
||||
.await?;
|
||||
let exists = select(exists(
|
||||
remote_image::table.filter(remote_image::link.eq(link_)),
|
||||
))
|
||||
.get_result::<bool>(conn)
|
||||
.await?;
|
||||
if exists {
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
|
@ -2,18 +2,26 @@ use crate::newtypes::{DbUrl, LocalUserId};
|
|||
#[cfg(feature = "full")]
|
||||
use crate::schema::{local_image, remote_image};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use std::fmt::Debug;
|
||||
use ts_rs::TS;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations))]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Identifiable, Associations, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = local_image))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::local_user::LocalUser))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(local_user_id)))]
|
||||
pub struct LocalImage {
|
||||
pub local_user_id: LocalUserId,
|
||||
pub pictrs_alias: String,
|
||||
|
|
|
@ -29,6 +29,7 @@ use lemmy_api::{
|
|||
get_captcha::get_captcha,
|
||||
list_banned::list_banned_users,
|
||||
list_logins::list_logins,
|
||||
list_media::list_media,
|
||||
login::login,
|
||||
logout::logout,
|
||||
notifications::{
|
||||
|
@ -320,7 +321,8 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
|||
.route("/totp/generate", web::post().to(generate_totp_secret))
|
||||
.route("/totp/update", web::post().to(update_totp))
|
||||
.route("/list_logins", web::get().to(list_logins))
|
||||
.route("/validate_auth", web::get().to(validate_auth)),
|
||||
.route("/validate_auth", web::get().to(validate_auth))
|
||||
.route("/list_media", web::get().to(list_media)),
|
||||
)
|
||||
// Admin Actions
|
||||
.service(
|
||||
|
|
Loading…
Reference in a new issue