mirror of https://github.com/LemmyNet/lemmy.git
Merge remote-tracking branch 'origin/main' into delete_local_image_on_delete_account
commit
ac2ebc378b
|
@ -27,7 +27,7 @@
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"lemmy-js-client": "0.19.4-alpha.8",
|
"lemmy-js-client": "0.19.4-alpha.13",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
|
|
|
@ -30,8 +30,8 @@ devDependencies:
|
||||||
specifier: ^29.5.0
|
specifier: ^29.5.0
|
||||||
version: 29.7.0(@types/node@20.11.27)
|
version: 29.7.0(@types/node@20.11.27)
|
||||||
lemmy-js-client:
|
lemmy-js-client:
|
||||||
specifier: 0.19.4-alpha.8
|
specifier: 0.19.4-alpha.13
|
||||||
version: 0.19.4-alpha.8
|
version: 0.19.4-alpha.13
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.2.5
|
specifier: ^3.2.5
|
||||||
version: 3.2.5
|
version: 3.2.5
|
||||||
|
@ -1044,10 +1044,6 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/asynckit@0.4.0:
|
|
||||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/babel-jest@29.7.0(@babel/core@7.23.9):
|
/babel-jest@29.7.0(@babel/core@7.23.9):
|
||||||
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
|
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
|
||||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
@ -1261,13 +1257,6 @@ packages:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/combined-stream@1.0.8:
|
|
||||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
delayed-stream: 1.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/concat-map@0.0.1:
|
/concat-map@0.0.1:
|
||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -1295,14 +1284,6 @@ packages:
|
||||||
- ts-node
|
- ts-node
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/cross-fetch@4.0.0:
|
|
||||||
resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
|
|
||||||
dependencies:
|
|
||||||
node-fetch: 2.7.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/cross-spawn@7.0.3:
|
/cross-spawn@7.0.3:
|
||||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -1342,11 +1323,6 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/delayed-stream@1.0.0:
|
|
||||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
|
||||||
engines: {node: '>=0.4.0'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/detect-newline@3.1.0:
|
/detect-newline@3.1.0:
|
||||||
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
|
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -1646,15 +1622,6 @@ packages:
|
||||||
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
|
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/form-data@4.0.0:
|
|
||||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
|
||||||
engines: {node: '>= 6'}
|
|
||||||
dependencies:
|
|
||||||
asynckit: 0.4.0
|
|
||||||
combined-stream: 1.0.8
|
|
||||||
mime-types: 2.1.35
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fs.realpath@1.0.0:
|
/fs.realpath@1.0.0:
|
||||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2390,13 +2357,8 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/lemmy-js-client@0.19.4-alpha.8:
|
/lemmy-js-client@0.19.4-alpha.13:
|
||||||
resolution: {integrity: sha512-8vjqUYVOhyUTcmG9FvPLjrWziVwNa2/Zi+kSflTrajJsK0V+5DclJ5dhdVMUQ4DEA70gb0OuNMDlipPG2FoS5A==}
|
resolution: {integrity: sha512-ru1dCqPSfOJdsGq7am5J7P7f+/hpyHGhNbCEV/JAZP2U1lGHul32gLpBkilDnStDNdeq52scjKx+3WskRJFGFA==}
|
||||||
dependencies:
|
|
||||||
cross-fetch: 4.0.0
|
|
||||||
form-data: 4.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/leven@3.1.0:
|
/leven@3.1.0:
|
||||||
|
@ -2485,18 +2447,6 @@ packages:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/mime-db@1.52.0:
|
|
||||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/mime-types@2.1.35:
|
|
||||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
mime-db: 1.52.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/mimic-fn@2.1.0:
|
/mimic-fn@2.1.0:
|
||||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -2523,18 +2473,6 @@ packages:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/node-fetch@2.7.0:
|
|
||||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
|
||||||
engines: {node: 4.x || >=6.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
encoding: ^0.1.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
encoding:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
whatwg-url: 5.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/node-int64@0.4.0:
|
/node-int64@0.4.0:
|
||||||
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
|
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2952,10 +2890,6 @@ packages:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/tr46@0.0.3:
|
|
||||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/ts-api-utils@1.3.0(typescript@5.4.2):
|
/ts-api-utils@1.3.0(typescript@5.4.2):
|
||||||
resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
|
resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
@ -3067,17 +3001,6 @@ packages:
|
||||||
makeerror: 1.0.12
|
makeerror: 1.0.12
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/webidl-conversions@3.0.1:
|
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/whatwg-url@5.0.0:
|
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
|
||||||
dependencies:
|
|
||||||
tr46: 0.0.3
|
|
||||||
webidl-conversions: 3.0.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/which@2.0.2:
|
/which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
|
@ -5,18 +5,18 @@ import {
|
||||||
setupLogins,
|
setupLogins,
|
||||||
resolveBetaCommunity,
|
resolveBetaCommunity,
|
||||||
followCommunity,
|
followCommunity,
|
||||||
unfollowRemotes,
|
|
||||||
getSite,
|
getSite,
|
||||||
waitUntil,
|
waitUntil,
|
||||||
beta,
|
beta,
|
||||||
betaUrl,
|
betaUrl,
|
||||||
registerUser,
|
registerUser,
|
||||||
|
unfollows,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
|
|
||||||
beforeAll(setupLogins);
|
beforeAll(setupLogins);
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
unfollowRemotes(alpha);
|
unfollows();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Follow local community", async () => {
|
test("Follow local community", async () => {
|
||||||
|
|
|
@ -14,25 +14,30 @@ import {
|
||||||
betaUrl,
|
betaUrl,
|
||||||
createCommunity,
|
createCommunity,
|
||||||
createPost,
|
createPost,
|
||||||
|
deleteAllImages,
|
||||||
delta,
|
delta,
|
||||||
epsilon,
|
epsilon,
|
||||||
gamma,
|
gamma,
|
||||||
getSite,
|
getSite,
|
||||||
|
imageFetchLimit,
|
||||||
registerUser,
|
registerUser,
|
||||||
resolveBetaCommunity,
|
resolveBetaCommunity,
|
||||||
resolvePost,
|
resolvePost,
|
||||||
setupLogins,
|
setupLogins,
|
||||||
unfollowRemotes,
|
unfollows,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
const downloadFileSync = require("download-file-sync");
|
const downloadFileSync = require("download-file-sync");
|
||||||
|
|
||||||
beforeAll(setupLogins);
|
beforeAll(setupLogins);
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
unfollowRemotes(alphaImage);
|
unfollows();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Upload image and delete it", async () => {
|
test("Upload image and delete it", async () => {
|
||||||
|
// Before running this test, you need to delete all previous images in the DB
|
||||||
|
await deleteAllImages(alpha);
|
||||||
|
|
||||||
// Upload test image. We use a simple string buffer as pictrs doesnt require an actual image
|
// Upload test image. We use a simple string buffer as pictrs doesnt require an actual image
|
||||||
// in testing mode.
|
// in testing mode.
|
||||||
const upload_form: UploadImage = {
|
const upload_form: UploadImage = {
|
||||||
|
@ -48,6 +53,24 @@ test("Upload image and delete it", async () => {
|
||||||
const content = downloadFileSync(upload.url);
|
const content = downloadFileSync(upload.url);
|
||||||
expect(content.length).toBeGreaterThan(0);
|
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);
|
||||||
|
|
||||||
|
// Ensure that it also comes back with the admin all images
|
||||||
|
const listAllMediaRes = await alphaImage.listAllMedia({
|
||||||
|
limit: imageFetchLimit,
|
||||||
|
});
|
||||||
|
|
||||||
|
// This number comes from all the previous thumbnails fetched in other tests.
|
||||||
|
const previousThumbnails = 1;
|
||||||
|
expect(listAllMediaRes.images.length).toBe(previousThumbnails);
|
||||||
|
|
||||||
|
// 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
|
// delete image
|
||||||
const delete_form: DeleteImage = {
|
const delete_form: DeleteImage = {
|
||||||
token: upload.files![0].delete_token,
|
token: upload.files![0].delete_token,
|
||||||
|
@ -59,6 +82,16 @@ test("Upload image and delete it", async () => {
|
||||||
// ensure that image is deleted
|
// ensure that image is deleted
|
||||||
const content2 = downloadFileSync(upload.url);
|
const content2 = downloadFileSync(upload.url);
|
||||||
expect(content2).toBe("");
|
expect(content2).toBe("");
|
||||||
|
|
||||||
|
// Ensure that it shows the image is deleted
|
||||||
|
const deletedListMediaRes = await alphaImage.listMedia();
|
||||||
|
expect(deletedListMediaRes.images.length).toBe(0);
|
||||||
|
|
||||||
|
// Ensure that the admin shows its deleted
|
||||||
|
const deletedListAllMediaRes = await alphaImage.listAllMedia({
|
||||||
|
limit: imageFetchLimit,
|
||||||
|
});
|
||||||
|
expect(deletedListAllMediaRes.images.length).toBe(previousThumbnails - 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Purge user, uploaded image removed", async () => {
|
test("Purge user, uploaded image removed", async () => {
|
||||||
|
@ -80,10 +113,10 @@ test("Purge user, uploaded image removed", async () => {
|
||||||
|
|
||||||
// purge user
|
// purge user
|
||||||
let site = await getSite(user);
|
let site = await getSite(user);
|
||||||
const purge_form: PurgePerson = {
|
const purgeForm: PurgePerson = {
|
||||||
person_id: site.my_user!.local_user_view.person.id,
|
person_id: site.my_user!.local_user_view.person.id,
|
||||||
};
|
};
|
||||||
const delete_ = await alphaImage.purgePerson(purge_form);
|
const delete_ = await alphaImage.purgePerson(purgeForm);
|
||||||
expect(delete_.success).toBe(true);
|
expect(delete_.success).toBe(true);
|
||||||
|
|
||||||
// ensure that image is deleted
|
// ensure that image is deleted
|
||||||
|
@ -117,10 +150,11 @@ test("Purge post, linked image removed", async () => {
|
||||||
expect(post.post_view.post.url).toBe(upload.url);
|
expect(post.post_view.post.url).toBe(upload.url);
|
||||||
|
|
||||||
// purge post
|
// purge post
|
||||||
const purge_form: PurgePost = {
|
|
||||||
|
const purgeForm: PurgePost = {
|
||||||
post_id: post.post_view.post.id,
|
post_id: post.post_view.post.id,
|
||||||
};
|
};
|
||||||
const delete_ = await beta.purgePost(purge_form);
|
const delete_ = await beta.purgePost(purgeForm);
|
||||||
expect(delete_.success).toBe(true);
|
expect(delete_.success).toBe(true);
|
||||||
|
|
||||||
// ensure that image is deleted
|
// ensure that image is deleted
|
||||||
|
|
|
@ -8,9 +8,9 @@ import {
|
||||||
editPrivateMessage,
|
editPrivateMessage,
|
||||||
listPrivateMessages,
|
listPrivateMessages,
|
||||||
deletePrivateMessage,
|
deletePrivateMessage,
|
||||||
unfollowRemotes,
|
|
||||||
waitUntil,
|
waitUntil,
|
||||||
reportPrivateMessage,
|
reportPrivateMessage,
|
||||||
|
unfollows,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
|
|
||||||
let recipient_id: number;
|
let recipient_id: number;
|
||||||
|
@ -22,7 +22,7 @@ beforeAll(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
unfollowRemotes(alpha);
|
unfollows();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Create a private message", async () => {
|
test("Create a private message", async () => {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
BlockInstanceResponse,
|
BlockInstanceResponse,
|
||||||
CommunityId,
|
CommunityId,
|
||||||
CreatePrivateMessageReport,
|
CreatePrivateMessageReport,
|
||||||
|
DeleteImage,
|
||||||
EditCommunity,
|
EditCommunity,
|
||||||
GetReplies,
|
GetReplies,
|
||||||
GetRepliesResponse,
|
GetRepliesResponse,
|
||||||
|
@ -79,6 +80,7 @@ import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails";
|
||||||
import { ListingType } from "lemmy-js-client/dist/types/ListingType";
|
import { ListingType } from "lemmy-js-client/dist/types/ListingType";
|
||||||
|
|
||||||
export const fetchFunction = fetch;
|
export const fetchFunction = fetch;
|
||||||
|
export const imageFetchLimit = 50;
|
||||||
|
|
||||||
export let alphaUrl = "http://127.0.0.1:8541";
|
export let alphaUrl = "http://127.0.0.1:8541";
|
||||||
export let betaUrl = "http://127.0.0.1:8551";
|
export let betaUrl = "http://127.0.0.1:8551";
|
||||||
|
@ -865,9 +867,25 @@ export function randomString(length: number): string {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteAllImages(api: LemmyHttp) {
|
||||||
|
const imagesRes = await api.listAllMedia({
|
||||||
|
limit: imageFetchLimit,
|
||||||
|
});
|
||||||
|
imagesRes.images;
|
||||||
|
|
||||||
|
for (const image of imagesRes.images) {
|
||||||
|
const form: DeleteImage = {
|
||||||
|
token: image.pictrs_delete_token,
|
||||||
|
filename: image.pictrs_alias,
|
||||||
|
};
|
||||||
|
await api.deleteImage(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function unfollows() {
|
export async function unfollows() {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
unfollowRemotes(alpha),
|
unfollowRemotes(alpha),
|
||||||
|
unfollowRemotes(beta),
|
||||||
unfollowRemotes(gamma),
|
unfollowRemotes(gamma),
|
||||||
unfollowRemotes(delta),
|
unfollowRemotes(delta),
|
||||||
unfollowRemotes(epsilon),
|
unfollowRemotes(epsilon),
|
||||||
|
|
|
@ -19,8 +19,9 @@ import {
|
||||||
getPost,
|
getPost,
|
||||||
getComments,
|
getComments,
|
||||||
fetchFunction,
|
fetchFunction,
|
||||||
|
alphaImage,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { LemmyHttp, SaveUserSettings } from "lemmy-js-client";
|
import { LemmyHttp, SaveUserSettings, UploadImage } from "lemmy-js-client";
|
||||||
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
||||||
|
|
||||||
beforeAll(setupLogins);
|
beforeAll(setupLogins);
|
||||||
|
@ -159,3 +160,34 @@ test("Create user with accept-language", async () => {
|
||||||
// which is automatically enabled by backend
|
// which is automatically enabled by backend
|
||||||
expect(langs).toStrictEqual(["und", "de", "en", "fr"]);
|
expect(langs).toStrictEqual(["und", "de", "en", "fr"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Set a new avatar, old avatar is deleted", async () => {
|
||||||
|
const listMediaRes = await alphaImage.listMedia();
|
||||||
|
expect(listMediaRes.images.length).toBe(0);
|
||||||
|
const upload_form1: UploadImage = {
|
||||||
|
image: Buffer.from("test1"),
|
||||||
|
};
|
||||||
|
const upload1 = await alphaImage.uploadImage(upload_form1);
|
||||||
|
expect(upload1.url).toBeDefined();
|
||||||
|
|
||||||
|
let form1 = {
|
||||||
|
avatar: upload1.url,
|
||||||
|
};
|
||||||
|
await saveUserSettings(alpha, form1);
|
||||||
|
const listMediaRes1 = await alphaImage.listMedia();
|
||||||
|
expect(listMediaRes1.images.length).toBe(1);
|
||||||
|
|
||||||
|
const upload_form2: UploadImage = {
|
||||||
|
image: Buffer.from("test2"),
|
||||||
|
};
|
||||||
|
const upload2 = await alphaImage.uploadImage(upload_form2);
|
||||||
|
expect(upload2.url).toBeDefined();
|
||||||
|
|
||||||
|
let form2 = {
|
||||||
|
avatar: upload1.url,
|
||||||
|
};
|
||||||
|
await saveUserSettings(alpha, form2);
|
||||||
|
// make sure only the new avatar is kept
|
||||||
|
const listMediaRes2 = await alphaImage.listMedia();
|
||||||
|
expect(listMediaRes2.images.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
#[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 get_captcha;
|
||||||
pub mod list_banned;
|
pub mod list_banned;
|
||||||
pub mod list_logins;
|
pub mod list_logins;
|
||||||
|
pub mod list_media;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod logout;
|
pub mod logout;
|
||||||
pub mod notifications;
|
pub mod notifications;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use actix_web::web::{Data, Json};
|
use activitypub_federation::config::Data;
|
||||||
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
person::SaveUserSettings,
|
person::SaveUserSettings,
|
||||||
|
request::replace_image,
|
||||||
utils::{
|
utils::{
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
|
@ -40,6 +42,8 @@ pub async fn save_user_settings(
|
||||||
let bio = diesel_option_overwrite(
|
let bio = diesel_option_overwrite(
|
||||||
process_markdown_opt(&data.bio, &slur_regex, &url_blocklist, &context).await?,
|
process_markdown_opt(&data.bio, &slur_regex, &url_blocklist, &context).await?,
|
||||||
);
|
);
|
||||||
|
replace_image(&data.avatar, &local_user_view.person.avatar, &context).await?;
|
||||||
|
replace_image(&data.banner, &local_user_view.person.banner, &context).await?;
|
||||||
|
|
||||||
let avatar = proxy_image_link_opt_api(&data.avatar, &context).await?;
|
let avatar = proxy_image_link_opt_api(&data.avatar, &context).await?;
|
||||||
let banner = proxy_image_link_opt_api(&data.banner, &context).await?;
|
let banner = proxy_image_link_opt_api(&data.banner, &context).await?;
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
use actix_web::web::{Data, Json, Query};
|
||||||
|
use lemmy_api_common::{
|
||||||
|
context::LemmyContext,
|
||||||
|
person::{ListMedia, ListMediaResponse},
|
||||||
|
utils::is_admin,
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::source::images::LocalImage;
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(context))]
|
||||||
|
pub async fn list_all_media(
|
||||||
|
data: Query<ListMedia>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
local_user_view: LocalUserView,
|
||||||
|
) -> Result<Json<ListMediaResponse>, LemmyError> {
|
||||||
|
// Only let admins view all media
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
|
let page = data.page;
|
||||||
|
let limit = data.limit;
|
||||||
|
let images = LocalImage::get_all(&mut context.pool(), page, limit).await?;
|
||||||
|
Ok(Json(ListMediaResponse { images }))
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod federated_instances;
|
pub mod federated_instances;
|
||||||
pub mod leave_admin;
|
pub mod leave_admin;
|
||||||
|
pub mod list_all_media;
|
||||||
pub mod mod_log;
|
pub mod mod_log;
|
||||||
pub mod purge;
|
pub mod purge;
|
||||||
pub mod registration_applications;
|
pub mod registration_applications;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::sensitive::Sensitive;
|
use crate::sensitive::Sensitive;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::{CommentReplyId, CommunityId, LanguageId, PersonId, PersonMentionId},
|
newtypes::{CommentReplyId, CommunityId, LanguageId, PersonId, PersonMentionId},
|
||||||
source::site::Site,
|
source::{images::LocalImage, site::Site},
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
ListingType,
|
ListingType,
|
||||||
PostListingMode,
|
PostListingMode,
|
||||||
|
@ -422,3 +422,20 @@ pub struct UpdateTotp {
|
||||||
pub struct UpdateTotpResponse {
|
pub struct UpdateTotpResponse {
|
||||||
pub enabled: bool,
|
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>,
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::{
|
||||||
post::{LinkMetadata, OpenGraphData},
|
post::{LinkMetadata, OpenGraphData},
|
||||||
utils::proxy_image_link,
|
utils::proxy_image_link,
|
||||||
};
|
};
|
||||||
|
use activitypub_federation::config::Data;
|
||||||
use encoding::{all::encodings, DecoderTrap};
|
use encoding::{all::encodings, DecoderTrap};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::DbUrl,
|
newtypes::DbUrl,
|
||||||
|
@ -312,6 +313,26 @@ async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Resu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When adding a new avatar or similar image, delete the old one.
|
||||||
|
pub async fn replace_image(
|
||||||
|
new_image: &Option<String>,
|
||||||
|
old_image: &Option<DbUrl>,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
if new_image.is_some() {
|
||||||
|
// Ignore errors because image may be stored externally.
|
||||||
|
if let Some(avatar) = &old_image {
|
||||||
|
let image = LocalImage::delete_by_url(&mut context.pool(), avatar)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
if let Some(image) = image {
|
||||||
|
delete_image_from_pictrs(&image.pictrs_alias, &image.pictrs_delete_token, context).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[allow(clippy::indexing_slicing)]
|
||||||
|
|
|
@ -4,6 +4,7 @@ use lemmy_api_common::{
|
||||||
build_response::build_community_response,
|
build_response::build_community_response,
|
||||||
community::{CommunityResponse, EditCommunity},
|
community::{CommunityResponse, EditCommunity},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
request::replace_image,
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_mod_action,
|
check_community_mod_action,
|
||||||
|
@ -42,6 +43,9 @@ pub async fn update_community(
|
||||||
let description =
|
let description =
|
||||||
process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context).await?;
|
process_markdown_opt(&data.description, &slur_regex, &url_blocklist, &context).await?;
|
||||||
is_valid_body_field(&data.description, false)?;
|
is_valid_body_field(&data.description, false)?;
|
||||||
|
let old_community = Community::read(&mut context.pool(), data.community_id).await?;
|
||||||
|
replace_image(&data.icon, &old_community.icon, &context).await?;
|
||||||
|
replace_image(&data.banner, &old_community.banner, &context).await?;
|
||||||
|
|
||||||
let description = diesel_option_overwrite(description);
|
let description = diesel_option_overwrite(description);
|
||||||
let icon = proxy_image_link_opt_api(&data.icon, &context).await?;
|
let icon = proxy_image_link_opt_api(&data.icon, &context).await?;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use crate::site::{application_question_check, site_default_post_listing_type_check};
|
use crate::site::{application_question_check, site_default_post_listing_type_check};
|
||||||
use actix_web::web::{Data, Json};
|
use activitypub_federation::config::Data;
|
||||||
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
request::replace_image,
|
||||||
site::{EditSite, SiteResponse},
|
site::{EditSite, SiteResponse},
|
||||||
utils::{
|
utils::{
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
|
@ -63,6 +65,9 @@ pub async fn update_site(
|
||||||
SiteLanguage::update(&mut context.pool(), discussion_languages.clone(), &site).await?;
|
SiteLanguage::update(&mut context.pool(), discussion_languages.clone(), &site).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replace_image(&data.icon, &site.icon, &context).await?;
|
||||||
|
replace_image(&data.banner, &site.banner, &context).await?;
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let sidebar = process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context).await?;
|
let sidebar = process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context).await?;
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::{DbUrl, LocalUserId},
|
newtypes::{DbUrl, LocalUserId},
|
||||||
schema::{
|
schema::{local_image, remote_image},
|
||||||
local_image::dsl::{local_image, local_user_id, pictrs_alias},
|
|
||||||
remote_image::dsl::{link, remote_image},
|
|
||||||
},
|
|
||||||
source::images::{LocalImage, LocalImageForm, RemoteImage, RemoteImageForm},
|
source::images::{LocalImage, LocalImageForm, RemoteImage, RemoteImageForm},
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, limit_and_offset, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::exists,
|
dsl::exists,
|
||||||
|
@ -15,7 +12,6 @@ use diesel::{
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
NotFound,
|
NotFound,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
Table,
|
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -23,29 +19,71 @@ use url::Url;
|
||||||
impl LocalImage {
|
impl LocalImage {
|
||||||
pub async fn create(pool: &mut DbPool<'_>, form: &LocalImageForm) -> Result<Self, Error> {
|
pub async fn create(pool: &mut DbPool<'_>, form: &LocalImageForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(local_image)
|
insert_into(local_image::table)
|
||||||
.values(form)
|
.values(form)
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
Self::get_all_helper(pool, Some(user_id), page, limit, false).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_all_by_local_user_id(
|
pub async fn get_all_by_local_user_id(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
user_id: &LocalUserId,
|
user_id: LocalUserId,
|
||||||
|
) -> Result<Vec<Self>, Error> {
|
||||||
|
Self::get_all_helper(pool, Some(user_id), None, None, true).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_all(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
page: Option<i64>,
|
||||||
|
limit: Option<i64>,
|
||||||
|
) -> Result<Vec<Self>, Error> {
|
||||||
|
Self::get_all_helper(pool, None, page, limit, false).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_all_helper(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
user_id: Option<LocalUserId>,
|
||||||
|
page: Option<i64>,
|
||||||
|
limit: Option<i64>,
|
||||||
|
ignore_page_limits: bool,
|
||||||
) -> Result<Vec<Self>, Error> {
|
) -> Result<Vec<Self>, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
local_image
|
let mut query = local_image::table
|
||||||
.filter(local_user_id.eq(user_id))
|
.select(local_image::all_columns)
|
||||||
.select(local_image::all_columns())
|
.order_by(local_image::published.desc())
|
||||||
.load::<LocalImage>(conn)
|
.into_boxed();
|
||||||
|
|
||||||
|
if let Some(user_id) = user_id {
|
||||||
|
query = query.filter(local_image::local_user_id.eq(user_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ignore_page_limits {
|
||||||
|
let (limit, offset) = limit_and_offset(page, limit)?;
|
||||||
|
query = query.limit(limit).offset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
query.load::<LocalImage>(conn).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_by_alias(pool: &mut DbPool<'_>, alias: &str) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
diesel::delete(local_image::table.filter(local_image::pictrs_alias.eq(alias)))
|
||||||
|
.get_result(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_by_alias(pool: &mut DbPool<'_>, alias: &str) -> Result<usize, Error> {
|
pub async fn delete_by_url(pool: &mut DbPool<'_>, url: &DbUrl) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let alias = url.as_str().split('/').last().ok_or(NotFound)?;
|
||||||
diesel::delete(local_image.filter(pictrs_alias.eq(alias)))
|
Self::delete_by_alias(pool, alias).await
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +94,7 @@ impl RemoteImage {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|url| RemoteImageForm { link: url.into() })
|
.map(|url| RemoteImageForm { link: url.into() })
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
insert_into(remote_image)
|
insert_into(remote_image::table)
|
||||||
.values(forms)
|
.values(forms)
|
||||||
.on_conflict_do_nothing()
|
.on_conflict_do_nothing()
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
|
@ -66,9 +104,11 @@ impl RemoteImage {
|
||||||
pub async fn validate(pool: &mut DbPool<'_>, link_: DbUrl) -> Result<(), Error> {
|
pub async fn validate(pool: &mut DbPool<'_>, link_: DbUrl) -> Result<(), Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
let exists = select(exists(remote_image.filter((link).eq(link_))))
|
let exists = select(exists(
|
||||||
.get_result::<bool>(conn)
|
remote_image::table.filter(remote_image::link.eq(link_)),
|
||||||
.await?;
|
))
|
||||||
|
.get_result::<bool>(conn)
|
||||||
|
.await?;
|
||||||
if exists {
|
if exists {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,16 +2,27 @@ use crate::newtypes::{DbUrl, LocalUserId};
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use crate::schema::{local_image, remote_image};
|
use crate::schema::{local_image, remote_image};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_with::skip_serializing_none;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
use ts_rs::TS;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[skip_serializing_none]
|
||||||
#[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(table_name = local_image))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "full",
|
feature = "full",
|
||||||
diesel(belongs_to(crate::source::local_user::LocalUser))
|
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 struct LocalImage {
|
||||||
pub local_user_id: Option<LocalUserId>,
|
pub local_user_id: Option<LocalUserId>,
|
||||||
pub pictrs_alias: String,
|
pub pictrs_alias: String,
|
||||||
|
|
|
@ -47,6 +47,16 @@ fn queries<'a>() -> Queries<
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let is_local_user_banned_from_community = |person_id| {
|
||||||
|
exists(
|
||||||
|
community_person_ban::table.filter(
|
||||||
|
community::id
|
||||||
|
.eq(community_person_ban::community_id)
|
||||||
|
.and(community_person_ban::person_id.eq(person_id)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let is_saved = |person_id| {
|
let is_saved = |person_id| {
|
||||||
exists(
|
exists(
|
||||||
comment_saved::table.filter(
|
comment_saved::table.filter(
|
||||||
|
@ -107,6 +117,14 @@ fn queries<'a>() -> Queries<
|
||||||
|
|
||||||
let all_joins = move |query: comment_reply::BoxedQuery<'a, Pg>,
|
let all_joins = move |query: comment_reply::BoxedQuery<'a, Pg>,
|
||||||
my_person_id: Option<PersonId>| {
|
my_person_id: Option<PersonId>| {
|
||||||
|
let is_local_user_banned_from_community_selection: Box<
|
||||||
|
dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>,
|
||||||
|
> = if let Some(person_id) = my_person_id {
|
||||||
|
Box::new(is_local_user_banned_from_community(person_id))
|
||||||
|
} else {
|
||||||
|
Box::new(false.into_sql::<sql_types::Bool>())
|
||||||
|
};
|
||||||
|
|
||||||
let score_selection: Box<
|
let score_selection: Box<
|
||||||
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::SmallInt>>,
|
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::SmallInt>>,
|
||||||
> = if let Some(person_id) = my_person_id {
|
> = if let Some(person_id) = my_person_id {
|
||||||
|
@ -153,6 +171,7 @@ fn queries<'a>() -> Queries<
|
||||||
aliases::person1.fields(person::all_columns),
|
aliases::person1.fields(person::all_columns),
|
||||||
comment_aggregates::all_columns,
|
comment_aggregates::all_columns,
|
||||||
is_creator_banned_from_community,
|
is_creator_banned_from_community,
|
||||||
|
is_local_user_banned_from_community_selection,
|
||||||
creator_is_moderator,
|
creator_is_moderator,
|
||||||
creator_is_admin,
|
creator_is_admin,
|
||||||
subscribed_type_selection,
|
subscribed_type_selection,
|
||||||
|
|
|
@ -47,6 +47,16 @@ fn queries<'a>() -> Queries<
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let is_local_user_banned_from_community = |person_id| {
|
||||||
|
exists(
|
||||||
|
community_person_ban::table.filter(
|
||||||
|
community::id
|
||||||
|
.eq(community_person_ban::community_id)
|
||||||
|
.and(community_person_ban::person_id.eq(person_id)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let is_saved = |person_id| {
|
let is_saved = |person_id| {
|
||||||
exists(
|
exists(
|
||||||
comment_saved::table.filter(
|
comment_saved::table.filter(
|
||||||
|
@ -107,6 +117,13 @@ fn queries<'a>() -> Queries<
|
||||||
|
|
||||||
let all_joins = move |query: person_mention::BoxedQuery<'a, Pg>,
|
let all_joins = move |query: person_mention::BoxedQuery<'a, Pg>,
|
||||||
my_person_id: Option<PersonId>| {
|
my_person_id: Option<PersonId>| {
|
||||||
|
let is_local_user_banned_from_community_selection: Box<
|
||||||
|
dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>,
|
||||||
|
> = if let Some(person_id) = my_person_id {
|
||||||
|
Box::new(is_local_user_banned_from_community(person_id))
|
||||||
|
} else {
|
||||||
|
Box::new(false.into_sql::<sql_types::Bool>())
|
||||||
|
};
|
||||||
let score_selection: Box<
|
let score_selection: Box<
|
||||||
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::SmallInt>>,
|
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::SmallInt>>,
|
||||||
> = if let Some(person_id) = my_person_id {
|
> = if let Some(person_id) = my_person_id {
|
||||||
|
@ -153,6 +170,7 @@ fn queries<'a>() -> Queries<
|
||||||
aliases::person1.fields(person::all_columns),
|
aliases::person1.fields(person::all_columns),
|
||||||
comment_aggregates::all_columns,
|
comment_aggregates::all_columns,
|
||||||
is_creator_banned_from_community,
|
is_creator_banned_from_community,
|
||||||
|
is_local_user_banned_from_community_selection,
|
||||||
creator_is_moderator,
|
creator_is_moderator,
|
||||||
creator_is_admin,
|
creator_is_admin,
|
||||||
subscribed_type_selection,
|
subscribed_type_selection,
|
||||||
|
|
|
@ -108,6 +108,7 @@ pub struct PersonMentionView {
|
||||||
pub recipient: Person,
|
pub recipient: Person,
|
||||||
pub counts: CommentAggregates,
|
pub counts: CommentAggregates,
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
|
pub banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
pub creator_is_admin: bool,
|
pub creator_is_admin: bool,
|
||||||
pub subscribed: SubscribedType,
|
pub subscribed: SubscribedType,
|
||||||
|
@ -131,6 +132,7 @@ pub struct CommentReplyView {
|
||||||
pub recipient: Person,
|
pub recipient: Person,
|
||||||
pub counts: CommentAggregates,
|
pub counts: CommentAggregates,
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
|
pub banned_from_community: bool,
|
||||||
pub creator_is_moderator: bool,
|
pub creator_is_moderator: bool,
|
||||||
pub creator_is_admin: bool,
|
pub creator_is_admin: bool,
|
||||||
pub subscribed: SubscribedType,
|
pub subscribed: SubscribedType,
|
||||||
|
|
|
@ -29,6 +29,7 @@ use lemmy_api::{
|
||||||
get_captcha::get_captcha,
|
get_captcha::get_captcha,
|
||||||
list_banned::list_banned_users,
|
list_banned::list_banned_users,
|
||||||
list_logins::list_logins,
|
list_logins::list_logins,
|
||||||
|
list_media::list_media,
|
||||||
login::login,
|
login::login,
|
||||||
logout::logout,
|
logout::logout,
|
||||||
notifications::{
|
notifications::{
|
||||||
|
@ -71,6 +72,7 @@ use lemmy_api::{
|
||||||
block::block_instance,
|
block::block_instance,
|
||||||
federated_instances::get_federated_instances,
|
federated_instances::get_federated_instances,
|
||||||
leave_admin::leave_admin,
|
leave_admin::leave_admin,
|
||||||
|
list_all_media::list_all_media,
|
||||||
mod_log::get_mod_log,
|
mod_log::get_mod_log,
|
||||||
purge::{
|
purge::{
|
||||||
comment::purge_comment,
|
comment::purge_comment,
|
||||||
|
@ -282,6 +284,12 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
.wrap(rate_limit.import_user_settings())
|
.wrap(rate_limit.import_user_settings())
|
||||||
.route(web::post().to(import_settings)),
|
.route(web::post().to(import_settings)),
|
||||||
)
|
)
|
||||||
|
// TODO, all the current account related actions under /user need to get moved here eventually
|
||||||
|
.service(
|
||||||
|
web::scope("/account")
|
||||||
|
.wrap(rate_limit.message())
|
||||||
|
.route("/list_media", web::get().to(list_media)),
|
||||||
|
)
|
||||||
// User actions
|
// User actions
|
||||||
.service(
|
.service(
|
||||||
web::scope("/user")
|
web::scope("/user")
|
||||||
|
@ -339,6 +347,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
"/registration_application/approve",
|
"/registration_application/approve",
|
||||||
web::put().to(approve_registration_application),
|
web::put().to(approve_registration_application),
|
||||||
)
|
)
|
||||||
|
.route("/list_all_media", web::get().to(list_all_media))
|
||||||
.service(
|
.service(
|
||||||
web::scope("/purge")
|
web::scope("/purge")
|
||||||
.route("/person", web::post().to(purge_person))
|
.route("/person", web::post().to(purge_person))
|
||||||
|
|
Loading…
Reference in New Issue