From 5e7b30ac6ad3109504c101a55547f3966ebb1470 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Sat, 7 Dec 2024 21:06:33 +0000 Subject: [PATCH] Reorganize api endpoints (fixes #2022) (#5216) * Reorganize api endpoints (fixes #2022) * scopes * move message rate limit * move rate limit * apply suggestions * move my_user to separate endpoint * remove taglines and custom emojis from siteview * routes for api v3 and v4 * remove new features from api v3 * js client * replace occurences of `v3`, fix routes * replace getSite().my_user with getMyUser() * update * update config * remove web:: * update * prettier * lockfile * v * fix settings * move block endpoints * more changes * fmt * update * change some routes * lockfile * Add comment about deprecation --- api_tests/package.json | 2 +- api_tests/pnpm-lock.yaml | 10 +- api_tests/prepare-drone-federation-test.sh | 10 +- api_tests/src/comment.spec.ts | 8 +- api_tests/src/community.spec.ts | 3 +- api_tests/src/follow.spec.ts | 9 +- api_tests/src/image.spec.ts | 17 +- api_tests/src/post.spec.ts | 10 +- api_tests/src/shared.ts | 16 +- api_tests/src/user.spec.ts | 40 +- config/defaults.hjson | 2 +- crates/api/src/community/block.rs | 2 +- crates/api/src/local_user/block.rs | 2 +- crates/api/src/local_user/mod.rs | 1 + .../user_block_instance.rs | 0 crates/api/src/site/leave_admin.rs | 4 +- crates/api/src/site/mod.rs | 1 - crates/api_common/README.md | 2 +- crates/api_common/src/site.rs | 8 +- crates/api_common/src/utils.rs | 6 +- crates/api_crud/src/site/read.rs | 72 +--- crates/api_crud/src/user/mod.rs | 1 + crates/api_crud/src/user/my_user.rs | 43 ++ crates/utils/src/settings/structs.rs | 2 +- .../utils/src/utils/markdown/image_links.rs | 14 +- crates/utils/src/utils/markdown/mod.rs | 10 +- src/{api_routes_http.rs => api_routes_v3.rs} | 81 +--- src/api_routes_v4.rs | 392 ++++++++++++++++++ src/lib.rs | 6 +- 29 files changed, 558 insertions(+), 216 deletions(-) rename crates/api/src/{site => local_user}/user_block_instance.rs (100%) create mode 100644 crates/api_crud/src/user/my_user.rs rename src/{api_routes_http.rs => api_routes_v3.rs} (83%) create mode 100644 src/api_routes_v4.rs diff --git a/api_tests/package.json b/api_tests/package.json index ef47bf192..7ea21d0ba 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -28,7 +28,7 @@ "eslint": "^9.14.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^29.5.0", - "lemmy-js-client": "0.20.0-instance-blocks.5", + "lemmy-js-client": "0.20.0-api-v4.16", "prettier": "^3.2.5", "ts-jest": "^29.1.0", "typescript": "^5.5.4", diff --git a/api_tests/pnpm-lock.yaml b/api_tests/pnpm-lock.yaml index a95e80726..496606e6c 100644 --- a/api_tests/pnpm-lock.yaml +++ b/api_tests/pnpm-lock.yaml @@ -30,8 +30,8 @@ importers: specifier: ^29.5.0 version: 29.7.0(@types/node@22.9.0) lemmy-js-client: - specifier: 0.20.0-instance-blocks.5 - version: 0.20.0-instance-blocks.5 + specifier: 0.20.0-api-v4.16 + version: 0.20.0-api-v4.16 prettier: specifier: ^3.2.5 version: 3.3.3 @@ -1167,8 +1167,8 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - lemmy-js-client@0.20.0-instance-blocks.5: - resolution: {integrity: sha512-wDuRFzg32lbbJr4cNmd+cbzjgw+okw2/d5AujYjAm4gv0OEFfsYhP3QQ2WscwUR5HJTdzsR7IIyiBnvmaEUzUw==} + lemmy-js-client@0.20.0-api-v4.16: + resolution: {integrity: sha512-9Wn7b8YT2KnEA286+RV1B3mLmecAynvAERoC0ZZiccfSgkEvd3rG9A5X9ejiPqp+JzDZJeisO57+Ut4QHr5oTw==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -3077,7 +3077,7 @@ snapshots: kleur@3.0.3: {} - lemmy-js-client@0.20.0-instance-blocks.5: {} + lemmy-js-client@0.20.0-api-v4.16: {} leven@3.1.0: {} diff --git a/api_tests/prepare-drone-federation-test.sh b/api_tests/prepare-drone-federation-test.sh index 65c4827d9..e5a4bc604 100755 --- a/api_tests/prepare-drone-federation-test.sh +++ b/api_tests/prepare-drone-federation-test.sh @@ -82,13 +82,13 @@ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \ target/lemmy_server >$LOG_DIR/lemmy_epsilon.out 2>&1 & echo "wait for all instances to start" -while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-alpha:8541/api/v3/site')" != "200" ]]; do sleep 1; done +while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-alpha:8541/api/v4/site')" != "200" ]]; do sleep 1; done echo "alpha started" -while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-beta:8551/api/v3/site')" != "200" ]]; do sleep 1; done +while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-beta:8551/api/v4/site')" != "200" ]]; do sleep 1; done echo "beta started" -while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-gamma:8561/api/v3/site')" != "200" ]]; do sleep 1; done +while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-gamma:8561/api/v4/site')" != "200" ]]; do sleep 1; done echo "gamma started" -while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-delta:8571/api/v3/site')" != "200" ]]; do sleep 1; done +while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-delta:8571/api/v4/site')" != "200" ]]; do sleep 1; done echo "delta started" -while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-epsilon:8581/api/v3/site')" != "200" ]]; do sleep 1; done +while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-epsilon:8581/api/v4/site')" != "200" ]]; do sleep 1; done echo "epsilon started. All started" diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts index edc588db7..5cf94aa03 100644 --- a/api_tests/src/comment.spec.ts +++ b/api_tests/src/comment.spec.ts @@ -699,10 +699,10 @@ test("Check that activity from another instance is sent to third instance", asyn test("Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedded comments, A subs to B, B updates the lowest level comment, A fetches both the post and all the inreplyto comments for that post.", async () => { // Unfollow all remote communities - let site = await unfollowRemotes(alpha); - expect( - site.my_user?.follows.filter(c => c.community.local == false).length, - ).toBe(0); + let my_user = await unfollowRemotes(alpha); + expect(my_user.follows.filter(c => c.community.local == false).length).toBe( + 0, + ); // B creates a post, and two comments, should be invisible to A let postOnBetaRes = await createPost(beta, 2); diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts index 3583308f0..2bb092088 100644 --- a/api_tests/src/community.spec.ts +++ b/api_tests/src/community.spec.ts @@ -32,6 +32,7 @@ import { longDelay, editCommunity, unfollows, + getMyUser, userBlockInstance, } from "./shared"; import { AdminAllowInstanceParams } from "lemmy-js-client/dist/types/AdminAllowInstanceParams"; @@ -226,7 +227,7 @@ test("Admin actions in remote community are not federated to origin", async () = if (!betaCommunity) { throw "Missing beta community"; } - let bannedUserInfo1 = (await getSite(gamma)).my_user?.local_user_view.person; + let bannedUserInfo1 = (await getMyUser(gamma)).local_user_view.person; if (!bannedUserInfo1) { throw "Missing banned user 1"; } diff --git a/api_tests/src/follow.spec.ts b/api_tests/src/follow.spec.ts index 22fdfa305..936ce2606 100644 --- a/api_tests/src/follow.spec.ts +++ b/api_tests/src/follow.spec.ts @@ -12,6 +12,7 @@ import { registerUser, unfollows, delay, + getMyUser, } from "./shared"; beforeAll(setupLogins); @@ -85,8 +86,8 @@ test("Follow federated community", async () => { ); // Check it from local - let site = await getSite(alpha); - let remoteCommunityId = site.my_user?.follows.find( + let my_user = await getMyUser(alpha); + let remoteCommunityId = my_user?.follows.find( c => c.community.local == false && c.community.id === betaCommunityInitial.community.id, @@ -102,9 +103,9 @@ test("Follow federated community", async () => { expect(unfollow.community_view.subscribed).toBe("NotSubscribed"); // Make sure you are unsubbed locally - let siteUnfollowCheck = await getSite(alpha); + let siteUnfollowCheck = await getMyUser(alpha); expect( - siteUnfollowCheck.my_user?.follows.find( + siteUnfollowCheck.follows.find( c => c.community.id === betaCommunityInitial.community.id, ), ).toBe(undefined); diff --git a/api_tests/src/image.spec.ts b/api_tests/src/image.spec.ts index 7ac6e7221..a3478081a 100644 --- a/api_tests/src/image.spec.ts +++ b/api_tests/src/image.spec.ts @@ -32,6 +32,7 @@ import { createPostWithThumbnail, sampleImage, sampleSite, + getMyUser, } from "./shared"; beforeAll(setupLogins); @@ -129,9 +130,9 @@ test("Purge user, uploaded image removed", async () => { expect(content.length).toBeGreaterThan(0); // purge user - let site = await getSite(user); + let my_user = await getMyUser(user); const purgeForm: PurgePerson = { - person_id: site.my_user!.local_user_view.person.id, + person_id: my_user.local_user_view.person.id, }; const delete_ = await alphaImage.purgePerson(purgeForm); expect(delete_.success).toBe(true); @@ -199,11 +200,11 @@ test("Images in remote image post are proxied if setting enabled", async () => { // remote image gets proxied after upload expect( post.thumbnail_url?.startsWith( - "http://lemmy-gamma:8561/api/v3/image_proxy?url", + "http://lemmy-gamma:8561/api/v4/image_proxy?url", ), ).toBeTruthy(); expect( - post.body?.startsWith("![](http://lemmy-gamma:8561/api/v3/image_proxy?url"), + post.body?.startsWith("![](http://lemmy-gamma:8561/api/v4/image_proxy?url"), ).toBeTruthy(); // Make sure that it ends with jpg, to be sure its an image @@ -222,12 +223,12 @@ test("Images in remote image post are proxied if setting enabled", async () => { expect( epsilonPost.thumbnail_url?.startsWith( - "http://lemmy-epsilon:8581/api/v3/image_proxy?url", + "http://lemmy-epsilon:8581/api/v4/image_proxy?url", ), ).toBeTruthy(); expect( epsilonPost.body?.startsWith( - "![](http://lemmy-epsilon:8581/api/v3/image_proxy?url", + "![](http://lemmy-epsilon:8581/api/v4/image_proxy?url", ), ).toBeTruthy(); @@ -249,7 +250,7 @@ test("Thumbnail of remote image link is proxied if setting enabled", async () => // remote image gets proxied after upload expect( post.thumbnail_url?.startsWith( - "http://lemmy-gamma:8561/api/v3/image_proxy?url", + "http://lemmy-gamma:8561/api/v4/image_proxy?url", ), ).toBeTruthy(); @@ -267,7 +268,7 @@ test("Thumbnail of remote image link is proxied if setting enabled", async () => expect( epsilonPost.thumbnail_url?.startsWith( - "http://lemmy-epsilon:8581/api/v3/image_proxy?url", + "http://lemmy-epsilon:8581/api/v4/image_proxy?url", ), ).toBeTruthy(); diff --git a/api_tests/src/post.spec.ts b/api_tests/src/post.spec.ts index a6063e0a2..4158bbdc7 100644 --- a/api_tests/src/post.spec.ts +++ b/api_tests/src/post.spec.ts @@ -38,6 +38,7 @@ import { alphaUrl, loginUser, createCommunity, + getMyUser, } from "./shared"; import { PostView } from "lemmy-js-client/dist/types/PostView"; import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams"; @@ -451,8 +452,7 @@ test("Enforce site ban federation for local user", async () => { // create a test user let alphaUserHttp = await registerUser(alpha, alphaUrl); - let alphaUserPerson = (await getSite(alphaUserHttp)).my_user?.local_user_view - .person; + let alphaUserPerson = (await getMyUser(alphaUserHttp)).local_user_view.person; let alphaUserActorId = alphaUserPerson?.actor_id; if (!alphaUserActorId) { throw "Missing alpha user actor id"; @@ -532,8 +532,7 @@ test("Enforce site ban federation for federated user", async () => { // create a test user let alphaUserHttp = await registerUser(alpha, alphaUrl); - let alphaUserPerson = (await getSite(alphaUserHttp)).my_user?.local_user_view - .person; + let alphaUserPerson = (await getMyUser(alphaUserHttp)).local_user_view.person; let alphaUserActorId = alphaUserPerson?.actor_id; if (!alphaUserActorId) { throw "Missing alpha user actor id"; @@ -563,8 +562,7 @@ test("Enforce site ban federation for federated user", async () => { expect(banAlphaOnBeta.banned).toBe(true); // The beta site ban should NOT be federated to alpha - let alphaPerson2 = (await getSite(alphaUserHttp)).my_user!.local_user_view - .person; + let alphaPerson2 = (await getMyUser(alphaUserHttp)).local_user_view.person; expect(alphaPerson2.banned).toBe(false); // existing alpha post should be removed on beta diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index 9b0662959..1ed13d9cf 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -16,6 +16,7 @@ import { LemmyHttp, ListCommunityPendingFollows, ListCommunityPendingFollowsResponse, + MyUserInfo, PersonId, PostView, PrivateMessageReportResponse, @@ -761,6 +762,10 @@ export async function getSite(api: LemmyHttp): Promise { return api.getSite(); } +export async function getMyUser(api: LemmyHttp): Promise { + return api.getMyUser(); +} + export async function listPrivateMessages( api: LemmyHttp, ): Promise { @@ -770,19 +775,16 @@ export async function listPrivateMessages( return api.getPrivateMessages(form); } -export async function unfollowRemotes( - api: LemmyHttp, -): Promise { +export async function unfollowRemotes(api: LemmyHttp): Promise { // Unfollow all remote communities - let site = await getSite(api); + let my_user = await getMyUser(api); let remoteFollowed = - site.my_user?.follows.filter(c => c.community.local == false) ?? []; + my_user.follows.filter(c => c.community.local == false) ?? []; await Promise.all( remoteFollowed.map(cu => followCommunity(api, false, cu.community.id)), ); - let siteRes = await getSite(api); - return siteRes; + return await getMyUser(api); } export async function followBeta(api: LemmyHttp): Promise { diff --git a/api_tests/src/user.spec.ts b/api_tests/src/user.spec.ts index 5425b97f7..f7f80aecb 100644 --- a/api_tests/src/user.spec.ts +++ b/api_tests/src/user.spec.ts @@ -22,6 +22,7 @@ import { alphaImage, unfollows, saveUserSettingsBio, + getMyUser, getPersonDetails, } from "./shared"; import { @@ -50,12 +51,9 @@ function assertUserFederation(userOne?: PersonView, userTwo?: PersonView) { test("Create user", async () => { let user = await registerUser(alpha, alphaUrl); - let site = await getSite(user); - expect(site.my_user).toBeDefined(); - if (!site.my_user) { - throw "Missing site user"; - } - apShortname = `${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`; + let my_user = await getMyUser(user); + expect(my_user).toBeDefined(); + apShortname = `${my_user.local_user_view.person.name}@lemmy-alpha:8541`; }); test("Set some user settings, check that they are federated", async () => { @@ -70,8 +68,8 @@ test("Set some user settings, check that they are federated", async () => { }; await saveUserSettings(beta, form); - let site = await getSite(beta); - expect(site.my_user?.local_user_view.local_user.theme).toBe("test"); + let my_user = await getMyUser(beta); + expect(my_user.local_user_view.local_user.theme).toBe("test"); }); test("Delete user", async () => { @@ -127,8 +125,10 @@ test("Requests with invalid auth should be treated as unauthenticated", async () headers: { Authorization: "Bearer foobar" }, fetchFunction, }); + await expect(getMyUser(invalid_auth)).rejects.toStrictEqual( + Error("incorrect_login"), + ); let site = await getSite(invalid_auth); - expect(site.my_user).toBeUndefined(); expect(site.site_view).toBeDefined(); let form: GetPosts = {}; @@ -141,12 +141,9 @@ test("Create user with Arabic name", async () => { const name = "تجريب" + Math.random().toString().slice(2, 10); let user = await registerUser(alpha, alphaUrl, name); - let site = await getSite(user); - expect(site.my_user).toBeDefined(); - if (!site.my_user) { - throw "Missing site user"; - } - apShortname = `${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`; + let my_user = await getMyUser(user); + expect(my_user).toBeDefined(); + apShortname = `${my_user.local_user_view.person.name}@lemmy-alpha:8541`; let betaPerson1 = (await resolvePerson(beta, apShortname)).person; expect(betaPerson1!.person.name).toBe(name); @@ -167,13 +164,12 @@ test("Create user with accept-language", async () => { }); let user = await registerUser(lemmy_http, alphaUrl); + let my_user = await getMyUser(user); + expect(my_user).toBeDefined(); + expect(my_user?.local_user_view.local_user.interface_language).toBe("fr"); let site = await getSite(user); - expect(site.my_user).toBeDefined(); - expect(site.my_user?.local_user_view.local_user.interface_language).toBe( - "fr", - ); let langs = site.all_languages - .filter(a => site.my_user?.discussion_languages.includes(a.id)) + .filter(a => my_user.discussion_languages.includes(a.id)) .map(l => l.code); // should have languages from accept header, as well as "undetermined" // which is automatically enabled by backend @@ -219,8 +215,8 @@ test("Set a new avatar, old avatar is deleted", async () => { // Now try to save a user settings, with the icon missing, // and make sure it doesn't clear the data, or delete the image await saveUserSettingsBio(alpha); - let site = await getSite(alpha); - expect(site.my_user?.local_user_view.person.avatar).toBe(upload2.url); + let my_user = await getMyUser(alpha); + expect(my_user.local_user_view.person.avatar).toBe(upload2.url); // make sure only the new avatar is kept const listMediaRes4 = await alphaImage.listMedia(); diff --git a/config/defaults.hjson b/config/defaults.hjson index 282b7957d..21a76d7a5 100644 --- a/config/defaults.hjson +++ b/config/defaults.hjson @@ -66,7 +66,7 @@ # or # If enabled, all images from remote domains are rewritten to pass through - # `/api/v3/image_proxy`, including embedded images in markdown. Images are stored temporarily + # `/api/v4/image_proxy`, including embedded images in markdown. Images are stored temporarily # in pict-rs for caching. This improves privacy as users don't expose their IP to untrusted # servers, and decreases load on other servers. However it increases bandwidth use for the # local server. diff --git a/crates/api/src/community/block.rs b/crates/api/src/community/block.rs index a6a48e2e7..d49872493 100644 --- a/crates/api/src/community/block.rs +++ b/crates/api/src/community/block.rs @@ -17,7 +17,7 @@ use lemmy_db_views_actor::structs::CommunityView; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] -pub async fn block_community( +pub async fn user_block_community( data: Json, context: Data, local_user_view: LocalUserView, diff --git a/crates/api/src/local_user/block.rs b/crates/api/src/local_user/block.rs index 250277be3..80532e897 100644 --- a/crates/api/src/local_user/block.rs +++ b/crates/api/src/local_user/block.rs @@ -12,7 +12,7 @@ use lemmy_db_views_actor::structs::PersonView; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] -pub async fn block_person( +pub async fn user_block_person( data: Json, context: Data, local_user_view: LocalUserView, diff --git a/crates/api/src/local_user/mod.rs b/crates/api/src/local_user/mod.rs index b1ee7c0b6..d3fc37a73 100644 --- a/crates/api/src/local_user/mod.rs +++ b/crates/api/src/local_user/mod.rs @@ -15,5 +15,6 @@ pub mod report_count; pub mod reset_password; pub mod save_settings; pub mod update_totp; +pub mod user_block_instance; pub mod validate_auth; pub mod verify_email; diff --git a/crates/api/src/site/user_block_instance.rs b/crates/api/src/local_user/user_block_instance.rs similarity index 100% rename from crates/api/src/site/user_block_instance.rs rename to crates/api/src/local_user/user_block_instance.rs diff --git a/crates/api/src/site/leave_admin.rs b/crates/api/src/site/leave_admin.rs index 86b80be3e..fde258dd2 100644 --- a/crates/api/src/site/leave_admin.rs +++ b/crates/api/src/site/leave_admin.rs @@ -69,14 +69,12 @@ pub async fn leave_admin( site_view, admins, version: VERSION.to_string(), - my_user: None, all_languages, discussion_languages, oauth_providers: Some(oauth_providers), admin_oauth_providers: None, blocked_urls, tagline, - taglines: vec![], - custom_emojis: vec![], + my_user: None, })) } diff --git a/crates/api/src/site/mod.rs b/crates/api/src/site/mod.rs index 52e882bb3..bab66f33b 100644 --- a/crates/api/src/site/mod.rs +++ b/crates/api/src/site/mod.rs @@ -6,4 +6,3 @@ pub mod list_all_media; pub mod mod_log; pub mod purge; pub mod registration_applications; -pub mod user_block_instance; diff --git a/crates/api_common/README.md b/crates/api_common/README.md index b4e7ad63b..ded59d34a 100644 --- a/crates/api_common/README.md +++ b/crates/api_common/README.md @@ -11,7 +11,7 @@ Here is an example using [reqwest](https://crates.io/crates/reqwest): }; let client = Client::new(); let response = client - .get("https://lemmy.ml/api/v3/post/list") + .get("https://lemmy.ml/api/v4/post/list") .query(¶ms) .send() .await?; diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 9babe423c..7f1000b14 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -429,7 +429,7 @@ pub struct EditSite { /// The response for a site. pub struct SiteResponse { pub site_view: SiteView, - /// deprecated, use field `tagline` or /api/v3/tagline/list + /// deprecated, use field `tagline` or /api/v4/tagline/list pub taglines: Vec<()>, } @@ -442,14 +442,10 @@ pub struct GetSiteResponse { pub site_view: SiteView, pub admins: Vec, pub version: String, - #[cfg_attr(feature = "full", ts(optional))] + #[cfg_attr(feature = "full", ts(skip))] pub my_user: Option, pub all_languages: Vec, pub discussion_languages: Vec, - /// deprecated, use field `tagline` or /api/v3/tagline/list - pub taglines: Vec<()>, - /// deprecated, use /api/v3/custom_emoji/list - pub custom_emojis: Vec<()>, /// If the site has any taglines, a random one is included here for displaying #[cfg_attr(feature = "full", ts(optional))] pub tagline: Option, diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 3b8a00197..2a5981b70 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -1125,7 +1125,7 @@ async fn proxy_image_link_internal( } } -/// Rewrite a link to go through `/api/v3/image_proxy` endpoint. This is only for remote urls and +/// Rewrite a link to go through `/api/v4/image_proxy` endpoint. This is only for remote urls and /// if image_proxy setting is enabled. pub async fn proxy_image_link(link: Url, context: &LemmyContext) -> LemmyResult { proxy_image_link_internal( @@ -1177,7 +1177,7 @@ fn build_proxied_image_url( protocol_and_hostname: &str, ) -> Result { Url::parse(&format!( - "{}/api/v3/image_proxy?url={}", + "{}/api/v4/image_proxy?url={}", protocol_and_hostname, encode(link.as_str()) )) @@ -1256,7 +1256,7 @@ mod tests { ) .await?; assert_eq!( - "https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Flemmy-beta%2Fimage.png", + "https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Flemmy-beta%2Fimage.png", proxied.as_str() ); diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs index 6bee0fda6..220fe1bd5 100644 --- a/crates/api_crud/src/site/read.rs +++ b/crates/api_crud/src/site/read.rs @@ -1,30 +1,32 @@ +use crate::user::my_user::get_my_user; use actix_web::web::{Data, Json}; -use lemmy_api_common::{ - context::LemmyContext, - site::{GetSiteResponse, MyUserInfo}, -}; +use lemmy_api_common::{context::LemmyContext, site::GetSiteResponse}; use lemmy_db_schema::source::{ - actor_language::{LocalUserLanguage, SiteLanguage}, - community_block::CommunityBlock, - instance_block::InstanceBlock, + actor_language::SiteLanguage, language::Language, local_site_url_blocklist::LocalSiteUrlBlocklist, oauth_provider::OAuthProvider, - person_block::PersonBlock, tagline::Tagline, }; use lemmy_db_views::structs::{LocalUserView, SiteView}; -use lemmy_db_views_actor::structs::{CommunityFollowerView, CommunityModeratorView, PersonView}; -use lemmy_utils::{ - build_cache, - error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, - CacheLock, - VERSION, -}; +use lemmy_db_views_actor::structs::PersonView; +use lemmy_utils::{build_cache, error::LemmyResult, CacheLock, VERSION}; use std::sync::LazyLock; #[tracing::instrument(skip(context))] -pub async fn get_site( +pub async fn get_site_v3( + local_user_view: Option, + context: Data, +) -> LemmyResult> { + let mut site = get_site_v4(local_user_view.clone(), context.clone()).await?; + if let Some(local_user_view) = local_user_view { + site.my_user = Some(get_my_user(local_user_view, context).await?.0); + } + Ok(site) +} + +#[tracing::instrument(skip(context))] +pub async fn get_site_v4( local_user_view: Option, context: Data, ) -> LemmyResult> { @@ -35,42 +37,6 @@ pub async fn get_site( .await .map_err(|e| anyhow::anyhow!("Failed to construct site response: {e}"))?; - // Build the local user with parallel queries and add it to site response - site_response.my_user = if let Some(ref local_user_view) = local_user_view { - let person_id = local_user_view.person.id; - let local_user_id = local_user_view.local_user.id; - let pool = &mut context.pool(); - - let ( - follows, - community_blocks, - instance_blocks, - person_blocks, - moderates, - discussion_languages, - ) = lemmy_db_schema::try_join_with_pool!(pool => ( - |pool| CommunityFollowerView::for_person(pool, person_id), - |pool| CommunityBlock::for_person(pool, person_id), - |pool| InstanceBlock::for_person(pool, person_id), - |pool| PersonBlock::for_person(pool, person_id), - |pool| CommunityModeratorView::for_person(pool, person_id, Some(&local_user_view.local_user)), - |pool| LocalUserLanguage::read(pool, local_user_id) - )) - .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; - - Some(MyUserInfo { - local_user_view: local_user_view.clone(), - follows, - moderates, - community_blocks, - instance_blocks, - person_blocks, - discussion_languages, - }) - } else { - None - }; - // filter oauth_providers for public access if !local_user_view .map(|l| l.local_user.admin) @@ -103,7 +69,5 @@ async fn read_site(context: &LemmyContext) -> LemmyResult { tagline, oauth_providers: Some(oauth_providers), admin_oauth_providers: Some(admin_oauth_providers), - taglines: vec![], - custom_emojis: vec![], }) } diff --git a/crates/api_crud/src/user/mod.rs b/crates/api_crud/src/user/mod.rs index da1aa3ace..e6e88e6cd 100644 --- a/crates/api_crud/src/user/mod.rs +++ b/crates/api_crud/src/user/mod.rs @@ -1,2 +1,3 @@ pub mod create; pub mod delete; +pub mod my_user; diff --git a/crates/api_crud/src/user/my_user.rs b/crates/api_crud/src/user/my_user.rs new file mode 100644 index 000000000..805c9dabb --- /dev/null +++ b/crates/api_crud/src/user/my_user.rs @@ -0,0 +1,43 @@ +use actix_web::web::{Data, Json}; +use lemmy_api_common::{context::LemmyContext, site::MyUserInfo}; +use lemmy_db_schema::source::{ + actor_language::LocalUserLanguage, + community_block::CommunityBlock, + instance_block::InstanceBlock, + person_block::PersonBlock, +}; +use lemmy_db_views::structs::LocalUserView; +use lemmy_db_views_actor::structs::{CommunityFollowerView, CommunityModeratorView}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; + +#[tracing::instrument(skip(context))] +pub async fn get_my_user( + local_user_view: LocalUserView, + context: Data, +) -> LemmyResult> { + // Build the local user with parallel queries and add it to site response + let person_id = local_user_view.person.id; + let local_user_id = local_user_view.local_user.id; + let pool = &mut context.pool(); + + let (follows, community_blocks, instance_blocks, person_blocks, moderates, discussion_languages) = + lemmy_db_schema::try_join_with_pool!(pool => ( + |pool| CommunityFollowerView::for_person(pool, person_id), + |pool| CommunityBlock::for_person(pool, person_id), + |pool| InstanceBlock::for_person(pool, person_id), + |pool| PersonBlock::for_person(pool, person_id), + |pool| CommunityModeratorView::for_person(pool, person_id, Some(&local_user_view.local_user)), + |pool| LocalUserLanguage::read(pool, local_user_id) + )) + .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; + + Ok(Json(MyUserInfo { + local_user_view: local_user_view.clone(), + follows, + moderates, + community_blocks, + instance_blocks, + person_blocks, + discussion_languages, + })) +} diff --git a/crates/utils/src/settings/structs.rs b/crates/utils/src/settings/structs.rs index ccc0da45b..cbbbcbfe5 100644 --- a/crates/utils/src/settings/structs.rs +++ b/crates/utils/src/settings/structs.rs @@ -120,7 +120,7 @@ pub enum PictrsImageMode { #[default] StoreLinkPreviews, /// If enabled, all images from remote domains are rewritten to pass through - /// `/api/v3/image_proxy`, including embedded images in markdown. Images are stored temporarily + /// `/api/v4/image_proxy`, including embedded images in markdown. Images are stored temporarily /// in pict-rs for caching. This improves privacy as users don't expose their IP to untrusted /// servers, and decreases load on other servers. However it increases bandwidth use for the /// local server. diff --git a/crates/utils/src/utils/markdown/image_links.rs b/crates/utils/src/utils/markdown/image_links.rs index 9dcea8da7..0990b1bc7 100644 --- a/crates/utils/src/utils/markdown/image_links.rs +++ b/crates/utils/src/utils/markdown/image_links.rs @@ -4,7 +4,7 @@ use markdown_it::{plugins::cmark::inline::image::Image, NodeValue}; use url::Url; use urlencoding::encode; -/// Rewrites all links to remote domains in markdown, so they go through `/api/v3/image_proxy`. +/// Rewrites all links to remote domains in markdown, so they go through `/api/v4/image_proxy`. pub fn markdown_rewrite_image_links(mut src: String) -> (String, Vec) { let links_offsets = find_urls::(&src); @@ -18,7 +18,7 @@ pub fn markdown_rewrite_image_links(mut src: String) -> (String, Vec) { // If link points to remote domain, replace with proxied link if parsed.domain() != Some(&SETTINGS.hostname) { let mut proxied = format!( - "{}/api/v3/image_proxy?url={}", + "{}/api/v4/image_proxy?url={}", SETTINGS.get_protocol_and_hostname(), encode(url), ); @@ -115,7 +115,7 @@ mod tests { ( "remote image proxied", "![link](http://example.com/image.jpg)", - "![link](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)", + "![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)", ), ( "local image unproxied", @@ -125,7 +125,7 @@ mod tests { ( "multiple image links", "![link](http://example.com/image1.jpg) ![link](http://example.com/image2.jpg)", - "![link](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage1.jpg) ![link](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage2.jpg)", + "![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage1.jpg) ![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage2.jpg)", ), ( "empty link handled", @@ -135,7 +135,7 @@ mod tests { ( "empty label handled", "![](http://example.com/image.jpg)", - "![](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" + "![](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" ), ( "invalid image link removed", @@ -145,12 +145,12 @@ mod tests { ( "label with nested markdown handled", "![a *b* c](http://example.com/image.jpg)", - "![a *b* c](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" + "![a *b* c](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" ), ( "custom emoji support", r#"![party-blob](https://www.hexbear.net/pictrs/image/83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"#, - r#"![party-blob](https://lemmy-alpha/api/v3/image_proxy?url=https%3A%2F%2Fwww.hexbear.net%2Fpictrs%2Fimage%2F83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"# + r#"![party-blob](https://lemmy-alpha/api/v4/image_proxy?url=https%3A%2F%2Fwww.hexbear.net%2Fpictrs%2Fimage%2F83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"# ) ]; diff --git a/crates/utils/src/utils/markdown/mod.rs b/crates/utils/src/utils/markdown/mod.rs index 3dfa8e9f1..ba509596e 100644 --- a/crates/utils/src/utils/markdown/mod.rs +++ b/crates/utils/src/utils/markdown/mod.rs @@ -141,7 +141,7 @@ mod tests { ( "remote image proxied", "![link](http://example.com/image.jpg)", - "![link](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)", + "![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)", ), ( "local image unproxied", @@ -151,7 +151,7 @@ mod tests { ( "multiple image links", "![link](http://example.com/image1.jpg) ![link](http://example.com/image2.jpg)", - "![link](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage1.jpg) ![link](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage2.jpg)", + "![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage1.jpg) ![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage2.jpg)", ), ( "empty link handled", @@ -161,7 +161,7 @@ mod tests { ( "empty label handled", "![](http://example.com/image.jpg)", - "![](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" + "![](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" ), ( "invalid image link removed", @@ -171,12 +171,12 @@ mod tests { ( "label with nested markdown handled", "![a *b* c](http://example.com/image.jpg)", - "![a *b* c](https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" + "![a *b* c](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" ), ( "custom emoji support", r#"![party-blob](https://www.hexbear.net/pictrs/image/83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"#, - r#"![party-blob](https://lemmy-alpha/api/v3/image_proxy?url=https%3A%2F%2Fwww.hexbear.net%2Fpictrs%2Fimage%2F83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"# + r#"![party-blob](https://lemmy-alpha/api/v4/image_proxy?url=https%3A%2F%2Fwww.hexbear.net%2Fpictrs%2Fimage%2F83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"# ) ]; diff --git a/src/api_routes_http.rs b/src/api_routes_v3.rs similarity index 83% rename from src/api_routes_http.rs rename to src/api_routes_v3.rs index 4a648da00..eefaf5b87 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_v3.rs @@ -14,21 +14,15 @@ use lemmy_api::{ community::{ add_mod::add_mod_to_community, ban::ban_from_community, - block::block_community, + block::user_block_community, follow::follow_community, hide::hide_community, - pending_follows::{ - approve::post_pending_follows_approve, - count::get_pending_follows_count, - list::get_pending_follows_list, - }, - random::get_random_community, transfer::transfer_community, }, local_user::{ add_admin::add_admin, ban_person::ban_from_site, - block::block_person, + block::user_block_person, change_password::change_password, change_password_after_reset::change_password_after_reset, generate_totp_secret::generate_totp_secret, @@ -50,6 +44,7 @@ use lemmy_api::{ reset_password::reset_password, save_settings::save_user_settings, update_totp::update_totp, + user_block_instance::user_block_instance, validate_auth::validate_auth, verify_email::verify_email, }, @@ -60,7 +55,6 @@ use lemmy_api::{ like::like_post, list_post_likes::list_post_likes, lock::lock_post, - mark_many_read::mark_posts_as_read, mark_read::mark_post_as_read, save::save_post, }, @@ -76,8 +70,6 @@ use lemmy_api::{ resolve::resolve_pm_report, }, site::{ - admin_allow_instance::admin_allow_instance, - admin_block_instance::admin_block_instance, federated_instances::get_federated_instances, leave_admin::leave_admin, list_all_media::list_all_media, @@ -94,7 +86,6 @@ use lemmy_api::{ list::list_registration_applications, unread_count::get_unread_registration_application_count, }, - user_block_instance::user_block_instance, }, sitemap::get_sitemap, }; @@ -116,14 +107,8 @@ use lemmy_api_crud::{ custom_emoji::{ create::create_custom_emoji, delete::delete_custom_emoji, - list::list_custom_emojis, update::update_custom_emoji, }, - oauth_provider::{ - create::create_oauth_provider, - delete::delete_oauth_provider, - update::update_oauth_provider, - }, post::{ create::create_post, delete::delete_post, @@ -137,17 +122,8 @@ use lemmy_api_crud::{ read::get_private_message, update::update_private_message, }, - site::{create::create_site, read::get_site, update::update_site}, - tagline::{ - create::create_tagline, - delete::delete_tagline, - list::list_taglines, - update::update_tagline, - }, - user::{ - create::{authenticate_with_oauth, register}, - delete::delete_account, - }, + site::{create::create_site, read::get_site_v3, update::update_site}, + user::{create::register, delete::delete_account}, }; use lemmy_apub::api::{ list_comments::list_comments, @@ -161,6 +137,9 @@ use lemmy_apub::api::{ use lemmy_routes::images::image_proxy; use lemmy_utils::rate_limit::RateLimitCell; +// Deprecated, use api v4 instead. +// When removing api v3, we also need to rewrite all links in database with +// `/api/v3/image_proxy` to use `/api/v4/image_proxy` instead. pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { cfg.service( scope("/api/v3") @@ -169,7 +148,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { .service( scope("/site") .wrap(rate_limit.message()) - .route("", get().to(get_site)) + .route("", get().to(get_site_v3)) // Admin Actions .route("", post().to(create_site)) .route("", put().to(update_site)) @@ -202,24 +181,16 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { .wrap(rate_limit.message()) .route("", get().to(get_community)) .route("", put().to(update_community)) - .route("/random", get().to(get_random_community)) .route("/hide", put().to(hide_community)) .route("/list", get().to(list_communities)) .route("/follow", post().to(follow_community)) - .route("/block", post().to(block_community)) + .route("/block", post().to(user_block_community)) .route("/delete", post().to(delete_community)) // Mod Actions .route("/remove", post().to(remove_community)) .route("/transfer", post().to(transfer_community)) .route("/ban_user", post().to(ban_from_community)) - .route("/mod", post().to(add_mod_to_community)) - .service( - scope("/pending_follows") - .wrap(rate_limit.message()) - .route("/count", get().to(get_pending_follows_count)) - .route("/list", get().to(get_pending_follows_list)) - .route("/approve", post().to(post_pending_follows_approve)), - ), + .route("/mod", post().to(add_mod_to_community)), ) .service( scope("/federated_instances") @@ -242,7 +213,6 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { .route("/delete", post().to(delete_post)) .route("/remove", post().to(remove_post)) .route("/mark_as_read", post().to(mark_post_as_read)) - .route("/mark_many_as_read", post().to(mark_posts_as_read)) .route("/hide", post().to(hide_post)) .route("/lock", post().to(lock_post)) .route("/feature", post().to(feature_post)) @@ -354,7 +324,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { // Admin action. I don't like that it's in /user .route("/ban", post().to(ban_from_site)) .route("/banned", get().to(list_banned_users)) - .route("/block", post().to(block_person)) + .route("/block", post().to(user_block_person)) // TODO Account actions. I don't like that they're in /user maybe /accounts .route("/logout", post().to(logout)) .route("/delete_account", post().to(delete_account)) @@ -400,37 +370,14 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { .route("/community", post().to(purge_community)) .route("/post", post().to(purge_post)) .route("/comment", post().to(purge_comment)), - ) - .service( - scope("/tagline") - .wrap(rate_limit.message()) - .route("", post().to(create_tagline)) - .route("", put().to(update_tagline)) - .route("/delete", post().to(delete_tagline)) - .route("/list", get().to(list_taglines)), - ) - .route("block_instance", post().to(admin_block_instance)) - .route("allow_instance", post().to(admin_allow_instance)), + ), ) .service( scope("/custom_emoji") .wrap(rate_limit.message()) .route("", post().to(create_custom_emoji)) .route("", put().to(update_custom_emoji)) - .route("/delete", post().to(delete_custom_emoji)) - .route("/list", get().to(list_custom_emojis)), - ) - .service( - scope("/oauth_provider") - .wrap(rate_limit.message()) - .route("", post().to(create_oauth_provider)) - .route("", put().to(update_oauth_provider)) - .route("/delete", post().to(delete_oauth_provider)), - ) - .service( - scope("/oauth") - .wrap(rate_limit.register()) - .route("/authenticate", post().to(authenticate_with_oauth)), + .route("/delete", post().to(delete_custom_emoji)), ), ); cfg.service( diff --git a/src/api_routes_v4.rs b/src/api_routes_v4.rs new file mode 100644 index 000000000..a9f71c9da --- /dev/null +++ b/src/api_routes_v4.rs @@ -0,0 +1,392 @@ +use actix_web::{guard, web::*}; +use lemmy_api::{ + comment::{ + distinguish::distinguish_comment, + like::like_comment, + list_comment_likes::list_comment_likes, + save::save_comment, + }, + comment_report::{ + create::create_comment_report, + list::list_comment_reports, + resolve::resolve_comment_report, + }, + community::{ + add_mod::add_mod_to_community, + ban::ban_from_community, + block::user_block_community, + follow::follow_community, + hide::hide_community, + pending_follows::{ + approve::post_pending_follows_approve, + count::get_pending_follows_count, + list::get_pending_follows_list, + }, + random::get_random_community, + transfer::transfer_community, + }, + local_user::{ + add_admin::add_admin, + ban_person::ban_from_site, + block::user_block_person, + change_password::change_password, + change_password_after_reset::change_password_after_reset, + generate_totp_secret::generate_totp_secret, + get_captcha::get_captcha, + list_banned::list_banned_users, + list_logins::list_logins, + list_media::list_media, + login::login, + logout::logout, + notifications::{ + list_mentions::list_mentions, + list_replies::list_replies, + mark_all_read::mark_all_notifications_read, + mark_mention_read::mark_person_mention_as_read, + mark_reply_read::mark_reply_as_read, + unread_count::unread_count, + }, + report_count::report_count, + reset_password::reset_password, + save_settings::save_user_settings, + update_totp::update_totp, + user_block_instance::user_block_instance, + validate_auth::validate_auth, + verify_email::verify_email, + }, + post::{ + feature::feature_post, + get_link_metadata::get_link_metadata, + hide::hide_post, + like::like_post, + list_post_likes::list_post_likes, + lock::lock_post, + mark_many_read::mark_posts_as_read, + mark_read::mark_post_as_read, + save::save_post, + }, + post_report::{ + create::create_post_report, + list::list_post_reports, + resolve::resolve_post_report, + }, + private_message::mark_read::mark_pm_as_read, + private_message_report::{ + create::create_pm_report, + list::list_pm_reports, + resolve::resolve_pm_report, + }, + site::{ + admin_allow_instance::admin_allow_instance, + admin_block_instance::admin_block_instance, + federated_instances::get_federated_instances, + leave_admin::leave_admin, + list_all_media::list_all_media, + mod_log::get_mod_log, + purge::{ + comment::purge_comment, + community::purge_community, + person::purge_person, + post::purge_post, + }, + registration_applications::{ + approve::approve_registration_application, + get::get_registration_application, + list::list_registration_applications, + unread_count::get_unread_registration_application_count, + }, + }, + sitemap::get_sitemap, +}; +use lemmy_api_crud::{ + comment::{ + create::create_comment, + delete::delete_comment, + read::get_comment, + remove::remove_comment, + update::update_comment, + }, + community::{ + create::create_community, + delete::delete_community, + list::list_communities, + remove::remove_community, + update::update_community, + }, + custom_emoji::{ + create::create_custom_emoji, + delete::delete_custom_emoji, + list::list_custom_emojis, + update::update_custom_emoji, + }, + oauth_provider::{ + create::create_oauth_provider, + delete::delete_oauth_provider, + update::update_oauth_provider, + }, + post::{ + create::create_post, + delete::delete_post, + read::get_post, + remove::remove_post, + update::update_post, + }, + private_message::{ + create::create_private_message, + delete::delete_private_message, + read::get_private_message, + update::update_private_message, + }, + site::{create::create_site, read::get_site_v4, update::update_site}, + tagline::{ + create::create_tagline, + delete::delete_tagline, + list::list_taglines, + update::update_tagline, + }, + user::{ + create::{authenticate_with_oauth, register}, + delete::delete_account, + my_user::get_my_user, + }, +}; +use lemmy_apub::api::{ + list_comments::list_comments, + list_posts::list_posts, + read_community::get_community, + read_person::read_person, + resolve_object::resolve_object, + search::search, + user_settings_backup::{export_settings, import_settings}, +}; +use lemmy_routes::images::image_proxy; +use lemmy_utils::rate_limit::RateLimitCell; + +pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { + cfg.service( + scope("/api/v4") + .wrap(rate_limit.message()) + .route("/image_proxy", get().to(image_proxy)) + // Site + .service( + scope("/site") + .route("", get().to(get_site_v4)) + .route("", post().to(create_site)) + .route("", put().to(update_site)), + ) + .route("/modlog", get().to(get_mod_log)) + .service( + resource("/search") + .wrap(rate_limit.search()) + .route(get().to(search)), + ) + .route("/resolve_object", get().to(resolve_object)) + // Community + .service( + resource("/community") + .guard(guard::Post()) + .wrap(rate_limit.register()) + .route(post().to(create_community)), + ) + .service( + scope("/community") + .route("", get().to(get_community)) + .route("", put().to(update_community)) + .route("/random", get().to(get_random_community)) + .route("/hide", put().to(hide_community)) + .route("/list", get().to(list_communities)) + .route("/follow", post().to(follow_community)) + .route("/delete", post().to(delete_community)) + // Mod Actions + .route("/remove", post().to(remove_community)) + .route("/transfer", post().to(transfer_community)) + .route("/ban_user", post().to(ban_from_community)) + .route("/mod", post().to(add_mod_to_community)) + .service( + scope("/pending_follows") + .route("/count", get().to(get_pending_follows_count)) + .route("/list", get().to(get_pending_follows_list)) + .route("/approve", post().to(post_pending_follows_approve)), + ), + ) + .route("/federated_instances", get().to(get_federated_instances)) + // Post + .service( + // Handle POST to /post separately to add the post() rate limitter + resource("/post") + .guard(guard::Post()) + .wrap(rate_limit.post()) + .route(post().to(create_post)), + ) + .service( + scope("/post") + .route("", get().to(get_post)) + .route("", put().to(update_post)) + .route("/delete", post().to(delete_post)) + .route("/remove", post().to(remove_post)) + .route("/mark_as_read", post().to(mark_post_as_read)) + .route("/mark_as_read/many", post().to(mark_posts_as_read)) + .route("/hide", post().to(hide_post)) + .route("/lock", post().to(lock_post)) + .route("/feature", post().to(feature_post)) + .route("/list", get().to(list_posts)) + .route("/like", post().to(like_post)) + .route("/like/list", get().to(list_post_likes)) + .route("/save", put().to(save_post)) + .route("/report", post().to(create_post_report)) + .route("/report/resolve", put().to(resolve_post_report)) + .route("/report/list", get().to(list_post_reports)) + .route("/site_metadata", get().to(get_link_metadata)), + ) + // Comment + .service( + // Handle POST to /comment separately to add the comment() rate limitter + resource("/comment") + .guard(guard::Post()) + .wrap(rate_limit.comment()) + .route(post().to(create_comment)), + ) + .service( + scope("/comment") + .route("", get().to(get_comment)) + .route("", put().to(update_comment)) + .route("/delete", post().to(delete_comment)) + .route("/remove", post().to(remove_comment)) + .route("/mark_as_read", post().to(mark_reply_as_read)) + .route("/distinguish", post().to(distinguish_comment)) + .route("/like", post().to(like_comment)) + .route("/like/list", get().to(list_comment_likes)) + .route("/save", put().to(save_comment)) + .route("/list", get().to(list_comments)) + .route("/report", post().to(create_comment_report)) + .route("/report/resolve", put().to(resolve_comment_report)) + .route("/report/list", get().to(list_comment_reports)), + ) + // Private Message + .service( + scope("/private_message") + .route("/list", get().to(get_private_message)) + .route("", post().to(create_private_message)) + .route("", put().to(update_private_message)) + .route("/delete", post().to(delete_private_message)) + .route("/mark_as_read", post().to(mark_pm_as_read)) + .route("/report", post().to(create_pm_report)) + .route("/report/resolve", put().to(resolve_pm_report)) + .route("/report/list", get().to(list_pm_reports)), + ) + // User + .service( + scope("/account/auth") + .guard(guard::Post()) + .wrap(rate_limit.register()) + .route("/register", post().to(register)) + .route("/login", post().to(login)) + .route("/logout", post().to(logout)) + .route("/password_reset", post().to(reset_password)) + .route("/get_captcha", get().to(get_captcha)) + .route("/password_change", post().to(change_password_after_reset)) + .route("/change_password", put().to(change_password)) + .route("/totp/generate", post().to(generate_totp_secret)) + .route("/totp/update", post().to(update_totp)) + .route("/verify_email", post().to(verify_email)), + ) + .route("/account/settings/save", put().to(save_user_settings)) + .service( + scope("/account/settings") + .wrap(rate_limit.import_user_settings()) + .route("/export", get().to(export_settings)) + .route("/import", post().to(import_settings)), + ) + .service( + scope("/account") + .route("", get().to(get_my_user)) + .route("/list_media", get().to(list_media)) + .route("/mention", get().to(list_mentions)) + .route("/replies", get().to(list_replies)) + .route("/delete", post().to(delete_account)) + .route( + "/mention/mark_as_read", + post().to(mark_person_mention_as_read), + ) + .route( + "/mention/mark_as_read/all", + post().to(mark_all_notifications_read), + ) + .route("/report_count", get().to(report_count)) + .route("/unread_count", get().to(unread_count)) + .route("/list_logins", get().to(list_logins)) + .route("/validate_auth", get().to(validate_auth)) + .service( + scope("/block") + .route("/person", post().to(user_block_person)) + .route("/community", post().to(user_block_community)) + .route("/instance", post().to(user_block_instance)), + ), + ) + // User actions + .route("/person", get().to(read_person)) + // Admin Actions + .service( + scope("/admin") + .route("/add", post().to(add_admin)) + .route( + "/registration_application/count", + get().to(get_unread_registration_application_count), + ) + .route( + "/registration_application/list", + get().to(list_registration_applications), + ) + .route( + "/registration_application/approve", + put().to(approve_registration_application), + ) + .route( + "/registration_application", + get().to(get_registration_application), + ) + .route("/list_all_media", get().to(list_all_media)) + .service( + scope("/purge") + .route("/person", post().to(purge_person)) + .route("/community", post().to(purge_community)) + .route("/post", post().to(purge_post)) + .route("/comment", post().to(purge_comment)), + ) + .service( + scope("/tagline") + .route("", post().to(create_tagline)) + .route("", put().to(update_tagline)) + .route("/delete", post().to(delete_tagline)) + .route("/list", get().to(list_taglines)), + ) + .route("/ban", post().to(ban_from_site)) + .route("/banned", get().to(list_banned_users)) + .route("/leave", post().to(leave_admin)) + .service( + scope("/instance") + .route("/block", post().to(admin_block_instance)) + .route("/allow", post().to(admin_allow_instance)), + ), + ) + .service( + scope("/custom_emoji") + .route("", post().to(create_custom_emoji)) + .route("", put().to(update_custom_emoji)) + .route("/delete", post().to(delete_custom_emoji)) + .route("/list", get().to(list_custom_emojis)), + ) + .service( + scope("/oauth_provider") + .route("", post().to(create_oauth_provider)) + .route("", put().to(update_oauth_provider)) + .route("/delete", post().to(delete_oauth_provider)), + ) + .service( + scope("/oauth") + .wrap(rate_limit.register()) + .route("/authenticate", post().to(authenticate_with_oauth)), + ) + .route("/sitemap.xml", get().to(get_sitemap)), + ); +} diff --git a/src/lib.rs b/src/lib.rs index 319efd224..5586b6159 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ -pub mod api_routes_http; +pub mod api_routes_v3; +pub mod api_routes_v4; pub mod code_migrations; pub mod prometheus_metrics; pub mod scheduled_tasks; @@ -318,7 +319,8 @@ fn create_http_server( // The routes app - .configure(|cfg| api_routes_http::config(cfg, &rate_limit_cell)) + .configure(|cfg| api_routes_v3::config(cfg, &rate_limit_cell)) + .configure(|cfg| api_routes_v4::config(cfg, &rate_limit_cell)) .configure(|cfg| { if federation_enabled { lemmy_apub::http::routes::config(cfg);