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
This commit is contained in:
Nutomic 2024-12-07 21:06:33 +00:00 committed by GitHub
parent adadb45ace
commit 5e7b30ac6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 558 additions and 216 deletions

View file

@ -28,7 +28,7 @@
"eslint": "^9.14.0", "eslint": "^9.14.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.20.0-instance-blocks.5", "lemmy-js-client": "0.20.0-api-v4.16",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"ts-jest": "^29.1.0", "ts-jest": "^29.1.0",
"typescript": "^5.5.4", "typescript": "^5.5.4",

View file

@ -30,8 +30,8 @@ importers:
specifier: ^29.5.0 specifier: ^29.5.0
version: 29.7.0(@types/node@22.9.0) version: 29.7.0(@types/node@22.9.0)
lemmy-js-client: lemmy-js-client:
specifier: 0.20.0-instance-blocks.5 specifier: 0.20.0-api-v4.16
version: 0.20.0-instance-blocks.5 version: 0.20.0-api-v4.16
prettier: prettier:
specifier: ^3.2.5 specifier: ^3.2.5
version: 3.3.3 version: 3.3.3
@ -1167,8 +1167,8 @@ packages:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'} engines: {node: '>=6'}
lemmy-js-client@0.20.0-instance-blocks.5: lemmy-js-client@0.20.0-api-v4.16:
resolution: {integrity: sha512-wDuRFzg32lbbJr4cNmd+cbzjgw+okw2/d5AujYjAm4gv0OEFfsYhP3QQ2WscwUR5HJTdzsR7IIyiBnvmaEUzUw==} resolution: {integrity: sha512-9Wn7b8YT2KnEA286+RV1B3mLmecAynvAERoC0ZZiccfSgkEvd3rG9A5X9ejiPqp+JzDZJeisO57+Ut4QHr5oTw==}
leven@3.1.0: leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
@ -3077,7 +3077,7 @@ snapshots:
kleur@3.0.3: {} 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: {} leven@3.1.0: {}

View file

@ -82,13 +82,13 @@ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
target/lemmy_server >$LOG_DIR/lemmy_epsilon.out 2>&1 & target/lemmy_server >$LOG_DIR/lemmy_epsilon.out 2>&1 &
echo "wait for all instances to start" 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" 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" 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" 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" 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" echo "epsilon started. All started"

View file

@ -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 () => { 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 // Unfollow all remote communities
let site = await unfollowRemotes(alpha); let my_user = await unfollowRemotes(alpha);
expect( expect(my_user.follows.filter(c => c.community.local == false).length).toBe(
site.my_user?.follows.filter(c => c.community.local == false).length, 0,
).toBe(0); );
// B creates a post, and two comments, should be invisible to A // B creates a post, and two comments, should be invisible to A
let postOnBetaRes = await createPost(beta, 2); let postOnBetaRes = await createPost(beta, 2);

View file

@ -32,6 +32,7 @@ import {
longDelay, longDelay,
editCommunity, editCommunity,
unfollows, unfollows,
getMyUser,
userBlockInstance, userBlockInstance,
} from "./shared"; } from "./shared";
import { AdminAllowInstanceParams } from "lemmy-js-client/dist/types/AdminAllowInstanceParams"; 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) { if (!betaCommunity) {
throw "Missing beta community"; 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) { if (!bannedUserInfo1) {
throw "Missing banned user 1"; throw "Missing banned user 1";
} }

View file

@ -12,6 +12,7 @@ import {
registerUser, registerUser,
unfollows, unfollows,
delay, delay,
getMyUser,
} from "./shared"; } from "./shared";
beforeAll(setupLogins); beforeAll(setupLogins);
@ -85,8 +86,8 @@ test("Follow federated community", async () => {
); );
// Check it from local // Check it from local
let site = await getSite(alpha); let my_user = await getMyUser(alpha);
let remoteCommunityId = site.my_user?.follows.find( let remoteCommunityId = my_user?.follows.find(
c => c =>
c.community.local == false && c.community.local == false &&
c.community.id === betaCommunityInitial.community.id, c.community.id === betaCommunityInitial.community.id,
@ -102,9 +103,9 @@ test("Follow federated community", async () => {
expect(unfollow.community_view.subscribed).toBe("NotSubscribed"); expect(unfollow.community_view.subscribed).toBe("NotSubscribed");
// Make sure you are unsubbed locally // Make sure you are unsubbed locally
let siteUnfollowCheck = await getSite(alpha); let siteUnfollowCheck = await getMyUser(alpha);
expect( expect(
siteUnfollowCheck.my_user?.follows.find( siteUnfollowCheck.follows.find(
c => c.community.id === betaCommunityInitial.community.id, c => c.community.id === betaCommunityInitial.community.id,
), ),
).toBe(undefined); ).toBe(undefined);

View file

@ -32,6 +32,7 @@ import {
createPostWithThumbnail, createPostWithThumbnail,
sampleImage, sampleImage,
sampleSite, sampleSite,
getMyUser,
} from "./shared"; } from "./shared";
beforeAll(setupLogins); beforeAll(setupLogins);
@ -129,9 +130,9 @@ test("Purge user, uploaded image removed", async () => {
expect(content.length).toBeGreaterThan(0); expect(content.length).toBeGreaterThan(0);
// purge user // purge user
let site = await getSite(user); let my_user = await getMyUser(user);
const purgeForm: PurgePerson = { 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); const delete_ = await alphaImage.purgePerson(purgeForm);
expect(delete_.success).toBe(true); 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 // remote image gets proxied after upload
expect( expect(
post.thumbnail_url?.startsWith( post.thumbnail_url?.startsWith(
"http://lemmy-gamma:8561/api/v3/image_proxy?url", "http://lemmy-gamma:8561/api/v4/image_proxy?url",
), ),
).toBeTruthy(); ).toBeTruthy();
expect( 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(); ).toBeTruthy();
// Make sure that it ends with jpg, to be sure its an image // 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( expect(
epsilonPost.thumbnail_url?.startsWith( epsilonPost.thumbnail_url?.startsWith(
"http://lemmy-epsilon:8581/api/v3/image_proxy?url", "http://lemmy-epsilon:8581/api/v4/image_proxy?url",
), ),
).toBeTruthy(); ).toBeTruthy();
expect( expect(
epsilonPost.body?.startsWith( epsilonPost.body?.startsWith(
"![](http://lemmy-epsilon:8581/api/v3/image_proxy?url", "![](http://lemmy-epsilon:8581/api/v4/image_proxy?url",
), ),
).toBeTruthy(); ).toBeTruthy();
@ -249,7 +250,7 @@ test("Thumbnail of remote image link is proxied if setting enabled", async () =>
// remote image gets proxied after upload // remote image gets proxied after upload
expect( expect(
post.thumbnail_url?.startsWith( post.thumbnail_url?.startsWith(
"http://lemmy-gamma:8561/api/v3/image_proxy?url", "http://lemmy-gamma:8561/api/v4/image_proxy?url",
), ),
).toBeTruthy(); ).toBeTruthy();
@ -267,7 +268,7 @@ test("Thumbnail of remote image link is proxied if setting enabled", async () =>
expect( expect(
epsilonPost.thumbnail_url?.startsWith( epsilonPost.thumbnail_url?.startsWith(
"http://lemmy-epsilon:8581/api/v3/image_proxy?url", "http://lemmy-epsilon:8581/api/v4/image_proxy?url",
), ),
).toBeTruthy(); ).toBeTruthy();

View file

@ -38,6 +38,7 @@ import {
alphaUrl, alphaUrl,
loginUser, loginUser,
createCommunity, createCommunity,
getMyUser,
} from "./shared"; } from "./shared";
import { PostView } from "lemmy-js-client/dist/types/PostView"; import { PostView } from "lemmy-js-client/dist/types/PostView";
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams"; 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 // create a test user
let alphaUserHttp = await registerUser(alpha, alphaUrl); let alphaUserHttp = await registerUser(alpha, alphaUrl);
let alphaUserPerson = (await getSite(alphaUserHttp)).my_user?.local_user_view let alphaUserPerson = (await getMyUser(alphaUserHttp)).local_user_view.person;
.person;
let alphaUserActorId = alphaUserPerson?.actor_id; let alphaUserActorId = alphaUserPerson?.actor_id;
if (!alphaUserActorId) { if (!alphaUserActorId) {
throw "Missing alpha user actor id"; throw "Missing alpha user actor id";
@ -532,8 +532,7 @@ test("Enforce site ban federation for federated user", async () => {
// create a test user // create a test user
let alphaUserHttp = await registerUser(alpha, alphaUrl); let alphaUserHttp = await registerUser(alpha, alphaUrl);
let alphaUserPerson = (await getSite(alphaUserHttp)).my_user?.local_user_view let alphaUserPerson = (await getMyUser(alphaUserHttp)).local_user_view.person;
.person;
let alphaUserActorId = alphaUserPerson?.actor_id; let alphaUserActorId = alphaUserPerson?.actor_id;
if (!alphaUserActorId) { if (!alphaUserActorId) {
throw "Missing alpha user actor id"; throw "Missing alpha user actor id";
@ -563,8 +562,7 @@ test("Enforce site ban federation for federated user", async () => {
expect(banAlphaOnBeta.banned).toBe(true); expect(banAlphaOnBeta.banned).toBe(true);
// The beta site ban should NOT be federated to alpha // The beta site ban should NOT be federated to alpha
let alphaPerson2 = (await getSite(alphaUserHttp)).my_user!.local_user_view let alphaPerson2 = (await getMyUser(alphaUserHttp)).local_user_view.person;
.person;
expect(alphaPerson2.banned).toBe(false); expect(alphaPerson2.banned).toBe(false);
// existing alpha post should be removed on beta // existing alpha post should be removed on beta

View file

@ -16,6 +16,7 @@ import {
LemmyHttp, LemmyHttp,
ListCommunityPendingFollows, ListCommunityPendingFollows,
ListCommunityPendingFollowsResponse, ListCommunityPendingFollowsResponse,
MyUserInfo,
PersonId, PersonId,
PostView, PostView,
PrivateMessageReportResponse, PrivateMessageReportResponse,
@ -761,6 +762,10 @@ export async function getSite(api: LemmyHttp): Promise<GetSiteResponse> {
return api.getSite(); return api.getSite();
} }
export async function getMyUser(api: LemmyHttp): Promise<MyUserInfo> {
return api.getMyUser();
}
export async function listPrivateMessages( export async function listPrivateMessages(
api: LemmyHttp, api: LemmyHttp,
): Promise<PrivateMessagesResponse> { ): Promise<PrivateMessagesResponse> {
@ -770,19 +775,16 @@ export async function listPrivateMessages(
return api.getPrivateMessages(form); return api.getPrivateMessages(form);
} }
export async function unfollowRemotes( export async function unfollowRemotes(api: LemmyHttp): Promise<MyUserInfo> {
api: LemmyHttp,
): Promise<GetSiteResponse> {
// Unfollow all remote communities // Unfollow all remote communities
let site = await getSite(api); let my_user = await getMyUser(api);
let remoteFollowed = let remoteFollowed =
site.my_user?.follows.filter(c => c.community.local == false) ?? []; my_user.follows.filter(c => c.community.local == false) ?? [];
await Promise.all( await Promise.all(
remoteFollowed.map(cu => followCommunity(api, false, cu.community.id)), remoteFollowed.map(cu => followCommunity(api, false, cu.community.id)),
); );
let siteRes = await getSite(api); return await getMyUser(api);
return siteRes;
} }
export async function followBeta(api: LemmyHttp): Promise<CommunityResponse> { export async function followBeta(api: LemmyHttp): Promise<CommunityResponse> {

View file

@ -22,6 +22,7 @@ import {
alphaImage, alphaImage,
unfollows, unfollows,
saveUserSettingsBio, saveUserSettingsBio,
getMyUser,
getPersonDetails, getPersonDetails,
} from "./shared"; } from "./shared";
import { import {
@ -50,12 +51,9 @@ function assertUserFederation(userOne?: PersonView, userTwo?: PersonView) {
test("Create user", async () => { test("Create user", async () => {
let user = await registerUser(alpha, alphaUrl); let user = await registerUser(alpha, alphaUrl);
let site = await getSite(user); let my_user = await getMyUser(user);
expect(site.my_user).toBeDefined(); expect(my_user).toBeDefined();
if (!site.my_user) { apShortname = `${my_user.local_user_view.person.name}@lemmy-alpha:8541`;
throw "Missing site user";
}
apShortname = `${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`;
}); });
test("Set some user settings, check that they are federated", async () => { 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); await saveUserSettings(beta, form);
let site = await getSite(beta); let my_user = await getMyUser(beta);
expect(site.my_user?.local_user_view.local_user.theme).toBe("test"); expect(my_user.local_user_view.local_user.theme).toBe("test");
}); });
test("Delete user", async () => { test("Delete user", async () => {
@ -127,8 +125,10 @@ test("Requests with invalid auth should be treated as unauthenticated", async ()
headers: { Authorization: "Bearer foobar" }, headers: { Authorization: "Bearer foobar" },
fetchFunction, fetchFunction,
}); });
await expect(getMyUser(invalid_auth)).rejects.toStrictEqual(
Error("incorrect_login"),
);
let site = await getSite(invalid_auth); let site = await getSite(invalid_auth);
expect(site.my_user).toBeUndefined();
expect(site.site_view).toBeDefined(); expect(site.site_view).toBeDefined();
let form: GetPosts = {}; let form: GetPosts = {};
@ -141,12 +141,9 @@ test("Create user with Arabic name", async () => {
const name = "تجريب" + Math.random().toString().slice(2, 10); const name = "تجريب" + Math.random().toString().slice(2, 10);
let user = await registerUser(alpha, alphaUrl, name); let user = await registerUser(alpha, alphaUrl, name);
let site = await getSite(user); let my_user = await getMyUser(user);
expect(site.my_user).toBeDefined(); expect(my_user).toBeDefined();
if (!site.my_user) { apShortname = `${my_user.local_user_view.person.name}@lemmy-alpha:8541`;
throw "Missing site user";
}
apShortname = `${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`;
let betaPerson1 = (await resolvePerson(beta, apShortname)).person; let betaPerson1 = (await resolvePerson(beta, apShortname)).person;
expect(betaPerson1!.person.name).toBe(name); 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 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); 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 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); .map(l => l.code);
// should have languages from accept header, as well as "undetermined" // should have languages from accept header, as well as "undetermined"
// which is automatically enabled by backend // 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, // Now try to save a user settings, with the icon missing,
// and make sure it doesn't clear the data, or delete the image // and make sure it doesn't clear the data, or delete the image
await saveUserSettingsBio(alpha); await saveUserSettingsBio(alpha);
let site = await getSite(alpha); let my_user = await getMyUser(alpha);
expect(site.my_user?.local_user_view.person.avatar).toBe(upload2.url); expect(my_user.local_user_view.person.avatar).toBe(upload2.url);
// make sure only the new avatar is kept // make sure only the new avatar is kept
const listMediaRes4 = await alphaImage.listMedia(); const listMediaRes4 = await alphaImage.listMedia();

View file

@ -66,7 +66,7 @@
# or # or
# If enabled, all images from remote domains are rewritten to pass through # 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 # 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 # servers, and decreases load on other servers. However it increases bandwidth use for the
# local server. # local server.

View file

@ -17,7 +17,7 @@ use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn block_community( pub async fn user_block_community(
data: Json<BlockCommunity>, data: Json<BlockCommunity>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: LocalUserView, local_user_view: LocalUserView,

View file

@ -12,7 +12,7 @@ use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn block_person( pub async fn user_block_person(
data: Json<BlockPerson>, data: Json<BlockPerson>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: LocalUserView, local_user_view: LocalUserView,

View file

@ -15,5 +15,6 @@ pub mod report_count;
pub mod reset_password; pub mod reset_password;
pub mod save_settings; pub mod save_settings;
pub mod update_totp; pub mod update_totp;
pub mod user_block_instance;
pub mod validate_auth; pub mod validate_auth;
pub mod verify_email; pub mod verify_email;

View file

@ -69,14 +69,12 @@ pub async fn leave_admin(
site_view, site_view,
admins, admins,
version: VERSION.to_string(), version: VERSION.to_string(),
my_user: None,
all_languages, all_languages,
discussion_languages, discussion_languages,
oauth_providers: Some(oauth_providers), oauth_providers: Some(oauth_providers),
admin_oauth_providers: None, admin_oauth_providers: None,
blocked_urls, blocked_urls,
tagline, tagline,
taglines: vec![], my_user: None,
custom_emojis: vec![],
})) }))
} }

View file

@ -6,4 +6,3 @@ 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;
pub mod user_block_instance;

View file

@ -11,7 +11,7 @@ Here is an example using [reqwest](https://crates.io/crates/reqwest):
}; };
let client = Client::new(); let client = Client::new();
let response = client let response = client
.get("https://lemmy.ml/api/v3/post/list") .get("https://lemmy.ml/api/v4/post/list")
.query(&params) .query(&params)
.send() .send()
.await?; .await?;

View file

@ -429,7 +429,7 @@ pub struct EditSite {
/// The response for a site. /// The response for a site.
pub struct SiteResponse { pub struct SiteResponse {
pub site_view: SiteView, 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<()>, pub taglines: Vec<()>,
} }
@ -442,14 +442,10 @@ pub struct GetSiteResponse {
pub site_view: SiteView, pub site_view: SiteView,
pub admins: Vec<PersonView>, pub admins: Vec<PersonView>,
pub version: String, pub version: String,
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(skip))]
pub my_user: Option<MyUserInfo>, pub my_user: Option<MyUserInfo>,
pub all_languages: Vec<Language>, pub all_languages: Vec<Language>,
pub discussion_languages: Vec<LanguageId>, pub discussion_languages: Vec<LanguageId>,
/// 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 /// If the site has any taglines, a random one is included here for displaying
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
pub tagline: Option<Tagline>, pub tagline: Option<Tagline>,

View file

@ -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. /// if image_proxy setting is enabled.
pub async fn proxy_image_link(link: Url, context: &LemmyContext) -> LemmyResult<DbUrl> { pub async fn proxy_image_link(link: Url, context: &LemmyContext) -> LemmyResult<DbUrl> {
proxy_image_link_internal( proxy_image_link_internal(
@ -1177,7 +1177,7 @@ fn build_proxied_image_url(
protocol_and_hostname: &str, protocol_and_hostname: &str,
) -> Result<Url, url::ParseError> { ) -> Result<Url, url::ParseError> {
Url::parse(&format!( Url::parse(&format!(
"{}/api/v3/image_proxy?url={}", "{}/api/v4/image_proxy?url={}",
protocol_and_hostname, protocol_and_hostname,
encode(link.as_str()) encode(link.as_str())
)) ))
@ -1256,7 +1256,7 @@ mod tests {
) )
.await?; .await?;
assert_eq!( 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() proxied.as_str()
); );

View file

@ -1,30 +1,32 @@
use crate::user::my_user::get_my_user;
use actix_web::web::{Data, Json}; use actix_web::web::{Data, Json};
use lemmy_api_common::{ use lemmy_api_common::{context::LemmyContext, site::GetSiteResponse};
context::LemmyContext,
site::{GetSiteResponse, MyUserInfo},
};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
actor_language::{LocalUserLanguage, SiteLanguage}, actor_language::SiteLanguage,
community_block::CommunityBlock,
instance_block::InstanceBlock,
language::Language, language::Language,
local_site_url_blocklist::LocalSiteUrlBlocklist, local_site_url_blocklist::LocalSiteUrlBlocklist,
oauth_provider::OAuthProvider, oauth_provider::OAuthProvider,
person_block::PersonBlock,
tagline::Tagline, tagline::Tagline,
}; };
use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_db_views_actor::structs::{CommunityFollowerView, CommunityModeratorView, PersonView}; use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::{ use lemmy_utils::{build_cache, error::LemmyResult, CacheLock, VERSION};
build_cache,
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
CacheLock,
VERSION,
};
use std::sync::LazyLock; use std::sync::LazyLock;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn get_site( pub async fn get_site_v3(
local_user_view: Option<LocalUserView>,
context: Data<LemmyContext>,
) -> LemmyResult<Json<GetSiteResponse>> {
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<LocalUserView>, local_user_view: Option<LocalUserView>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<Json<GetSiteResponse>> { ) -> LemmyResult<Json<GetSiteResponse>> {
@ -35,42 +37,6 @@ pub async fn get_site(
.await .await
.map_err(|e| anyhow::anyhow!("Failed to construct site response: {e}"))?; .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 // filter oauth_providers for public access
if !local_user_view if !local_user_view
.map(|l| l.local_user.admin) .map(|l| l.local_user.admin)
@ -103,7 +69,5 @@ async fn read_site(context: &LemmyContext) -> LemmyResult<GetSiteResponse> {
tagline, tagline,
oauth_providers: Some(oauth_providers), oauth_providers: Some(oauth_providers),
admin_oauth_providers: Some(admin_oauth_providers), admin_oauth_providers: Some(admin_oauth_providers),
taglines: vec![],
custom_emojis: vec![],
}) })
} }

View file

@ -1,2 +1,3 @@
pub mod create; pub mod create;
pub mod delete; pub mod delete;
pub mod my_user;

View file

@ -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<LemmyContext>,
) -> LemmyResult<Json<MyUserInfo>> {
// 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,
}))
}

View file

@ -120,7 +120,7 @@ pub enum PictrsImageMode {
#[default] #[default]
StoreLinkPreviews, StoreLinkPreviews,
/// If enabled, all images from remote domains are rewritten to pass through /// 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 /// 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 /// servers, and decreases load on other servers. However it increases bandwidth use for the
/// local server. /// local server.

View file

@ -4,7 +4,7 @@ use markdown_it::{plugins::cmark::inline::image::Image, NodeValue};
use url::Url; use url::Url;
use urlencoding::encode; 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<Url>) { pub fn markdown_rewrite_image_links(mut src: String) -> (String, Vec<Url>) {
let links_offsets = find_urls::<Image>(&src); let links_offsets = find_urls::<Image>(&src);
@ -18,7 +18,7 @@ pub fn markdown_rewrite_image_links(mut src: String) -> (String, Vec<Url>) {
// If link points to remote domain, replace with proxied link // If link points to remote domain, replace with proxied link
if parsed.domain() != Some(&SETTINGS.hostname) { if parsed.domain() != Some(&SETTINGS.hostname) {
let mut proxied = format!( let mut proxied = format!(
"{}/api/v3/image_proxy?url={}", "{}/api/v4/image_proxy?url={}",
SETTINGS.get_protocol_and_hostname(), SETTINGS.get_protocol_and_hostname(),
encode(url), encode(url),
); );
@ -115,7 +115,7 @@ mod tests {
( (
"remote image proxied", "remote image proxied",
"![link](http://example.com/image.jpg)", "![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", "local image unproxied",
@ -125,7 +125,7 @@ mod tests {
( (
"multiple image links", "multiple image links",
"![link](http://example.com/image1.jpg) ![link](http://example.com/image2.jpg)", "![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", "empty link handled",
@ -135,7 +135,7 @@ mod tests {
( (
"empty label handled", "empty label handled",
"![](http://example.com/image.jpg)", "![](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", "invalid image link removed",
@ -145,12 +145,12 @@ mod tests {
( (
"label with nested markdown handled", "label with nested markdown handled",
"![a *b* c](http://example.com/image.jpg)", "![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", "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://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")"#
) )
]; ];

View file

@ -141,7 +141,7 @@ mod tests {
( (
"remote image proxied", "remote image proxied",
"![link](http://example.com/image.jpg)", "![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", "local image unproxied",
@ -151,7 +151,7 @@ mod tests {
( (
"multiple image links", "multiple image links",
"![link](http://example.com/image1.jpg) ![link](http://example.com/image2.jpg)", "![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", "empty link handled",
@ -161,7 +161,7 @@ mod tests {
( (
"empty label handled", "empty label handled",
"![](http://example.com/image.jpg)", "![](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", "invalid image link removed",
@ -171,12 +171,12 @@ mod tests {
( (
"label with nested markdown handled", "label with nested markdown handled",
"![a *b* c](http://example.com/image.jpg)", "![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", "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://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")"#
) )
]; ];

View file

@ -14,21 +14,15 @@ use lemmy_api::{
community::{ community::{
add_mod::add_mod_to_community, add_mod::add_mod_to_community,
ban::ban_from_community, ban::ban_from_community,
block::block_community, block::user_block_community,
follow::follow_community, follow::follow_community,
hide::hide_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, transfer::transfer_community,
}, },
local_user::{ local_user::{
add_admin::add_admin, add_admin::add_admin,
ban_person::ban_from_site, ban_person::ban_from_site,
block::block_person, block::user_block_person,
change_password::change_password, change_password::change_password,
change_password_after_reset::change_password_after_reset, change_password_after_reset::change_password_after_reset,
generate_totp_secret::generate_totp_secret, generate_totp_secret::generate_totp_secret,
@ -50,6 +44,7 @@ use lemmy_api::{
reset_password::reset_password, reset_password::reset_password,
save_settings::save_user_settings, save_settings::save_user_settings,
update_totp::update_totp, update_totp::update_totp,
user_block_instance::user_block_instance,
validate_auth::validate_auth, validate_auth::validate_auth,
verify_email::verify_email, verify_email::verify_email,
}, },
@ -60,7 +55,6 @@ use lemmy_api::{
like::like_post, like::like_post,
list_post_likes::list_post_likes, list_post_likes::list_post_likes,
lock::lock_post, lock::lock_post,
mark_many_read::mark_posts_as_read,
mark_read::mark_post_as_read, mark_read::mark_post_as_read,
save::save_post, save::save_post,
}, },
@ -76,8 +70,6 @@ use lemmy_api::{
resolve::resolve_pm_report, resolve::resolve_pm_report,
}, },
site::{ site::{
admin_allow_instance::admin_allow_instance,
admin_block_instance::admin_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, list_all_media::list_all_media,
@ -94,7 +86,6 @@ use lemmy_api::{
list::list_registration_applications, list::list_registration_applications,
unread_count::get_unread_registration_application_count, unread_count::get_unread_registration_application_count,
}, },
user_block_instance::user_block_instance,
}, },
sitemap::get_sitemap, sitemap::get_sitemap,
}; };
@ -116,14 +107,8 @@ use lemmy_api_crud::{
custom_emoji::{ custom_emoji::{
create::create_custom_emoji, create::create_custom_emoji,
delete::delete_custom_emoji, delete::delete_custom_emoji,
list::list_custom_emojis,
update::update_custom_emoji, update::update_custom_emoji,
}, },
oauth_provider::{
create::create_oauth_provider,
delete::delete_oauth_provider,
update::update_oauth_provider,
},
post::{ post::{
create::create_post, create::create_post,
delete::delete_post, delete::delete_post,
@ -137,17 +122,8 @@ use lemmy_api_crud::{
read::get_private_message, read::get_private_message,
update::update_private_message, update::update_private_message,
}, },
site::{create::create_site, read::get_site, update::update_site}, site::{create::create_site, read::get_site_v3, update::update_site},
tagline::{ user::{create::register, delete::delete_account},
create::create_tagline,
delete::delete_tagline,
list::list_taglines,
update::update_tagline,
},
user::{
create::{authenticate_with_oauth, register},
delete::delete_account,
},
}; };
use lemmy_apub::api::{ use lemmy_apub::api::{
list_comments::list_comments, list_comments::list_comments,
@ -161,6 +137,9 @@ use lemmy_apub::api::{
use lemmy_routes::images::image_proxy; use lemmy_routes::images::image_proxy;
use lemmy_utils::rate_limit::RateLimitCell; 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) { pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
cfg.service( cfg.service(
scope("/api/v3") scope("/api/v3")
@ -169,7 +148,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
.service( .service(
scope("/site") scope("/site")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", get().to(get_site)) .route("", get().to(get_site_v3))
// Admin Actions // Admin Actions
.route("", post().to(create_site)) .route("", post().to(create_site))
.route("", put().to(update_site)) .route("", put().to(update_site))
@ -202,24 +181,16 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", get().to(get_community)) .route("", get().to(get_community))
.route("", put().to(update_community)) .route("", put().to(update_community))
.route("/random", get().to(get_random_community))
.route("/hide", put().to(hide_community)) .route("/hide", put().to(hide_community))
.route("/list", get().to(list_communities)) .route("/list", get().to(list_communities))
.route("/follow", post().to(follow_community)) .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)) .route("/delete", post().to(delete_community))
// Mod Actions // Mod Actions
.route("/remove", post().to(remove_community)) .route("/remove", post().to(remove_community))
.route("/transfer", post().to(transfer_community)) .route("/transfer", post().to(transfer_community))
.route("/ban_user", post().to(ban_from_community)) .route("/ban_user", post().to(ban_from_community))
.route("/mod", post().to(add_mod_to_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)),
),
) )
.service( .service(
scope("/federated_instances") scope("/federated_instances")
@ -242,7 +213,6 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
.route("/delete", post().to(delete_post)) .route("/delete", post().to(delete_post))
.route("/remove", post().to(remove_post)) .route("/remove", post().to(remove_post))
.route("/mark_as_read", post().to(mark_post_as_read)) .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("/hide", post().to(hide_post))
.route("/lock", post().to(lock_post)) .route("/lock", post().to(lock_post))
.route("/feature", post().to(feature_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 // Admin action. I don't like that it's in /user
.route("/ban", post().to(ban_from_site)) .route("/ban", post().to(ban_from_site))
.route("/banned", get().to(list_banned_users)) .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 // TODO Account actions. I don't like that they're in /user maybe /accounts
.route("/logout", post().to(logout)) .route("/logout", post().to(logout))
.route("/delete_account", post().to(delete_account)) .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("/community", post().to(purge_community))
.route("/post", post().to(purge_post)) .route("/post", post().to(purge_post))
.route("/comment", post().to(purge_comment)), .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( .service(
scope("/custom_emoji") scope("/custom_emoji")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", post().to(create_custom_emoji)) .route("", post().to(create_custom_emoji))
.route("", put().to(update_custom_emoji)) .route("", put().to(update_custom_emoji))
.route("/delete", post().to(delete_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)),
), ),
); );
cfg.service( cfg.service(

392
src/api_routes_v4.rs Normal file
View file

@ -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)),
);
}

View file

@ -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 code_migrations;
pub mod prometheus_metrics; pub mod prometheus_metrics;
pub mod scheduled_tasks; pub mod scheduled_tasks;
@ -318,7 +319,8 @@ fn create_http_server(
// The routes // The routes
app 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| { .configure(|cfg| {
if federation_enabled { if federation_enabled {
lemmy_apub::http::routes::config(cfg); lemmy_apub::http::routes::config(cfg);