From 90bfe88dac538b1ae8214853f66050e5c5594f55 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Tue, 28 Jul 2026 15:47:21 +0100 Subject: [PATCH] feat: blocking api interfaces --- apps/app-frontend/src/helpers/users.ts | 14 +++++ apps/app/build.rs | 3 + apps/app/src/api/users.rs | 18 ++++++ packages/api-client/src/modules/index.ts | 4 ++ .../labrinth/blocked-users/internal.ts | 25 ++++++++ .../src/modules/labrinth/blocked-users/v3.ts | 45 ++++++++++++++ .../api-client/src/modules/labrinth/types.ts | 12 ++++ packages/app-lib/src/api/users.rs | 60 +++++++++++++++++++ 8 files changed, 181 insertions(+) create mode 100644 packages/api-client/src/modules/labrinth/blocked-users/internal.ts create mode 100644 packages/api-client/src/modules/labrinth/blocked-users/v3.ts diff --git a/apps/app-frontend/src/helpers/users.ts b/apps/app-frontend/src/helpers/users.ts index 800818b4e9..405fc3ea7d 100644 --- a/apps/app-frontend/src/helpers/users.ts +++ b/apps/app-frontend/src/helpers/users.ts @@ -54,3 +54,17 @@ export async function patch_user( ): Promise { await invoke('plugin:users|patch_user', { userId, patch }) } + +export async function block_user(userId: string): Promise { + await invoke('plugin:users|block_user', { userId }) +} + +export async function unblock_user(userId: string): Promise { + await invoke('plugin:users|unblock_user', { userId }) +} + +export async function get_blocked_users(): Promise { + return await invoke( + 'plugin:users|get_blocked_users', + ) +} diff --git a/apps/app/build.rs b/apps/app/build.rs index 514b389e19..2d2bfc899b 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -285,6 +285,9 @@ fn main() { "get_user_organizations", "get_user_collections", "patch_user", + "block_user", + "unblock_user", + "get_blocked_users", ]) .default_permission( DefaultPermissionRule::AllowAllCommands, diff --git a/apps/app/src/api/users.rs b/apps/app/src/api/users.rs index 8160ce7642..a7041f77f6 100644 --- a/apps/app/src/api/users.rs +++ b/apps/app/src/api/users.rs @@ -32,6 +32,21 @@ pub async fn patch_user(user_id: &str, patch: Value) -> Result<()> { Ok(theseus::users::patch_user(user_id, patch).await?) } +#[tauri::command] +pub async fn block_user(user_id: &str) -> Result<()> { + Ok(theseus::users::block_user(user_id).await?) +} + +#[tauri::command] +pub async fn unblock_user(user_id: &str) -> Result<()> { + Ok(theseus::users::unblock_user(user_id).await?) +} + +#[tauri::command] +pub async fn get_blocked_users() -> Result> { + Ok(theseus::users::get_blocked_users().await?) +} + pub fn init() -> tauri::plugin::TauriPlugin { tauri::plugin::Builder::new("users") .invoke_handler(tauri::generate_handler![ @@ -41,6 +56,9 @@ pub fn init() -> tauri::plugin::TauriPlugin { get_user_organizations, get_user_collections, patch_user, + block_user, + unblock_user, + get_blocked_users, ]) .build() } diff --git a/packages/api-client/src/modules/index.ts b/packages/api-client/src/modules/index.ts index 01ab392f6b..9f8167bbc8 100644 --- a/packages/api-client/src/modules/index.ts +++ b/packages/api-client/src/modules/index.ts @@ -24,6 +24,8 @@ import { LabrinthAttributionInternalModule } from './labrinth/attribution/intern import { LabrinthAuthInternalModule } from './labrinth/auth/internal' import { LabrinthAuthV2Module } from './labrinth/auth/v2' import { LabrinthBillingInternalModule } from './labrinth/billing/internal' +import { LabrinthBlockedUsersInternalModule } from './labrinth/blocked-users/internal' +import { LabrinthBlockedUsersV3Module } from './labrinth/blocked-users/v3' import { LabrinthCampaignInternalModule } from './labrinth/campaign/internal' import { LabrinthCollectionsModule } from './labrinth/collections' import { LabrinthContentV3Module } from './labrinth/content/v3' @@ -100,6 +102,8 @@ export const MODULE_REGISTRY = { labrinth_auth_v2: LabrinthAuthV2Module, labrinth_attribution_internal: LabrinthAttributionInternalModule, labrinth_billing_internal: LabrinthBillingInternalModule, + labrinth_blocked_users_internal: LabrinthBlockedUsersInternalModule, + labrinth_blocked_users_v3: LabrinthBlockedUsersV3Module, labrinth_campaign_internal: LabrinthCampaignInternalModule, labrinth_collections: LabrinthCollectionsModule, labrinth_content_v3: LabrinthContentV3Module, diff --git a/packages/api-client/src/modules/labrinth/blocked-users/internal.ts b/packages/api-client/src/modules/labrinth/blocked-users/internal.ts new file mode 100644 index 0000000000..3937bdb5a6 --- /dev/null +++ b/packages/api-client/src/modules/labrinth/blocked-users/internal.ts @@ -0,0 +1,25 @@ +import { AbstractModule } from '../../../core/abstract-module' +import type { Labrinth } from '../types' + +export class LabrinthBlockedUsersInternalModule extends AbstractModule { + public getModuleID(): string { + return 'labrinth_blocked_users_internal' + } + + /** + * Check whether one user has blocked another. + */ + public async getStatus( + userId: string, + targetId: string, + ): Promise { + return this.client.request( + `/block/${encodeURIComponent(userId)}/${encodeURIComponent(targetId)}`, + { + api: 'labrinth', + version: 'internal', + method: 'GET', + }, + ) + } +} diff --git a/packages/api-client/src/modules/labrinth/blocked-users/v3.ts b/packages/api-client/src/modules/labrinth/blocked-users/v3.ts new file mode 100644 index 0000000000..92ffd19eb8 --- /dev/null +++ b/packages/api-client/src/modules/labrinth/blocked-users/v3.ts @@ -0,0 +1,45 @@ +import { AbstractModule } from '../../../core/abstract-module' +import type { Labrinth } from '../types' + +export class LabrinthBlockedUsersV3Module extends AbstractModule { + public getModuleID(): string { + return 'labrinth_blocked_users_v3' + } + + /** + * List the users blocked by the authenticated user. + */ + public async list(): Promise { + return this.client.request('/blocks', { + api: 'labrinth', + version: 3, + method: 'GET', + }) + } + + /** + * Block a user. + * + * @param idOrUsername - The target user's ID or username + */ + public async block(idOrUsername: string): Promise { + return this.client.request(`/block/${encodeURIComponent(idOrUsername)}`, { + api: 'labrinth', + version: 3, + method: 'POST', + }) + } + + /** + * Unblock a user. + * + * @param idOrUsername - The target user's ID or username + */ + public async unblock(idOrUsername: string): Promise { + return this.client.request(`/block/${encodeURIComponent(idOrUsername)}`, { + api: 'labrinth', + version: 3, + method: 'DELETE', + }) + } +} diff --git a/packages/api-client/src/modules/labrinth/types.ts b/packages/api-client/src/modules/labrinth/types.ts index 337d1d1256..a5de5b8d8d 100644 --- a/packages/api-client/src/modules/labrinth/types.ts +++ b/packages/api-client/src/modules/labrinth/types.ts @@ -1691,6 +1691,18 @@ export namespace Labrinth { } } + export namespace BlockedUsers { + export namespace Internal { + export type BlockStatus = { + blocked: boolean + } + } + + export namespace v3 { + export type BlockedUserId = string + } + } + export namespace ServerPing { export namespace Internal { export type MinecraftJavaPingRequest = { diff --git a/packages/app-lib/src/api/users.rs b/packages/app-lib/src/api/users.rs index e3e1c6fdec..b92a00b7b1 100644 --- a/packages/app-lib/src/api/users.rs +++ b/packages/app-lib/src/api/users.rs @@ -129,3 +129,63 @@ pub async fn patch_user(user_id: &str, patch: Value) -> crate::Result<()> { Ok(()) } + +#[tracing::instrument] +pub async fn block_user(user_id: &str) -> crate::Result<()> { + let state = State::get().await?; + let user_id = urlencoding::encode(user_id); + + fetch_advanced( + Method::POST, + &format!("{}block/{}", env!("MODRINTH_API_URL_V3"), user_id), + None, + None, + None, + None, + None, + Some("/v3/block/:id"), + &state.api_semaphore, + &state.pool, + ) + .await?; + + Ok(()) +} + +#[tracing::instrument] +pub async fn unblock_user(user_id: &str) -> crate::Result<()> { + let state = State::get().await?; + let user_id = urlencoding::encode(user_id); + + fetch_advanced( + Method::DELETE, + &format!("{}block/{}", env!("MODRINTH_API_URL_V3"), user_id), + None, + None, + None, + None, + None, + Some("/v3/block/:id"), + &state.api_semaphore, + &state.pool, + ) + .await?; + + Ok(()) +} + +#[tracing::instrument] +pub async fn get_blocked_users() -> crate::Result> { + let state = State::get().await?; + + fetch_json( + Method::GET, + &format!("{}blocks", env!("MODRINTH_API_URL_V3")), + None, + None, + Some("/v3/blocks"), + &state.api_semaphore, + &state.pool, + ) + .await +}