mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-02-09 21:13:09 +00:00
231 lines
6.8 KiB
Rust
231 lines
6.8 KiB
Rust
//! Federation API endpoints.
|
|
|
|
use axum::{
|
|
extract::{Path, State},
|
|
http::StatusCode,
|
|
routing::{get, post},
|
|
Json, Router,
|
|
};
|
|
use serde::Deserialize;
|
|
use serde_json::{json, Value};
|
|
use sqlx::PgPool;
|
|
use uuid::Uuid;
|
|
|
|
use crate::auth::AuthUser;
|
|
use crate::plugins::builtin::federation::{
|
|
CommunityFederation, FederatedInstance, FederationService,
|
|
};
|
|
|
|
// ============================================================================
|
|
// Request Types
|
|
// ============================================================================
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct RegisterInstanceRequest {
|
|
pub url: String,
|
|
pub name: String,
|
|
pub description: Option<String>,
|
|
pub public_key: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct CreateFederationRequest {
|
|
pub remote_instance_id: Uuid,
|
|
pub remote_community_id: Uuid,
|
|
pub remote_community_name: String,
|
|
pub sync_direction: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct SetTrustLevelRequest {
|
|
pub trust_level: i32,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct IncomingFederationRequest {
|
|
pub from_instance_url: String,
|
|
pub from_community_name: String,
|
|
pub message: Option<String>,
|
|
}
|
|
|
|
// ============================================================================
|
|
// Handlers
|
|
// ============================================================================
|
|
|
|
/// Get all federated instances
|
|
async fn get_instances(
|
|
_auth: AuthUser,
|
|
State(pool): State<PgPool>,
|
|
) -> Result<Json<Vec<FederatedInstance>>, (StatusCode, String)> {
|
|
let instances = FederationService::get_instances(&pool)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(instances))
|
|
}
|
|
|
|
/// Register a new federated instance
|
|
async fn register_instance(
|
|
_auth: AuthUser,
|
|
State(pool): State<PgPool>,
|
|
Json(req): Json<RegisterInstanceRequest>,
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
|
let instance_id = FederationService::register_instance(
|
|
&pool,
|
|
&req.url,
|
|
&req.name,
|
|
req.description.as_deref(),
|
|
req.public_key.as_deref(),
|
|
)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(json!({"id": instance_id})))
|
|
}
|
|
|
|
/// Get federation statistics
|
|
async fn get_stats(
|
|
_auth: AuthUser,
|
|
Path(community_id): Path<Uuid>,
|
|
State(pool): State<PgPool>,
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
|
let stats = FederationService::get_stats(&pool, community_id)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(stats))
|
|
}
|
|
|
|
/// Get community federations
|
|
async fn get_community_federations(
|
|
_auth: AuthUser,
|
|
Path(community_id): Path<Uuid>,
|
|
State(pool): State<PgPool>,
|
|
) -> Result<Json<Vec<CommunityFederation>>, (StatusCode, String)> {
|
|
let federations = FederationService::get_community_federations(&pool, community_id)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(federations))
|
|
}
|
|
|
|
/// Request federation with another community
|
|
async fn request_federation(
|
|
_auth: AuthUser,
|
|
Path(community_id): Path<Uuid>,
|
|
State(pool): State<PgPool>,
|
|
Json(req): Json<CreateFederationRequest>,
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
|
let federation_id = FederationService::create_federation(
|
|
&pool,
|
|
community_id,
|
|
req.remote_instance_id,
|
|
req.remote_community_id,
|
|
&req.remote_community_name,
|
|
&req.sync_direction,
|
|
)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(json!({"id": federation_id})))
|
|
}
|
|
|
|
/// Approve a federation request
|
|
async fn approve_federation(
|
|
auth: AuthUser,
|
|
Path(federation_id): Path<Uuid>,
|
|
State(pool): State<PgPool>,
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
|
FederationService::approve_federation(&pool, federation_id, auth.user_id)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(json!({"success": true})))
|
|
}
|
|
|
|
/// Set trust level for an instance
|
|
async fn set_trust_level(
|
|
_auth: AuthUser,
|
|
Path(instance_id): Path<Uuid>,
|
|
State(pool): State<PgPool>,
|
|
Json(req): Json<SetTrustLevelRequest>,
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
|
FederationService::set_trust_level(&pool, instance_id, req.trust_level)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(json!({"success": true})))
|
|
}
|
|
|
|
/// Handle incoming federation request from another instance
|
|
/// Note: This endpoint is intentionally unauthenticated for server-to-server federation.
|
|
/// The request creates a PENDING entry that must be approved by a community admin.
|
|
/// TODO: Add rate limiting and instance signature verification for production.
|
|
async fn receive_federation_request(
|
|
Path(community_id): Path<Uuid>,
|
|
State(pool): State<PgPool>,
|
|
Json(req): Json<IncomingFederationRequest>,
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
|
// Validate the community exists and accepts federation requests
|
|
let community_exists = sqlx::query_scalar!(
|
|
"SELECT EXISTS(SELECT 1 FROM communities WHERE id = $1 AND is_active = true) as \"exists!\"",
|
|
community_id
|
|
)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
if !community_exists {
|
|
return Err((
|
|
StatusCode::NOT_FOUND,
|
|
"Community not found or inactive".to_string(),
|
|
));
|
|
}
|
|
|
|
let request_id = FederationService::request_federation(
|
|
&pool,
|
|
&req.from_instance_url,
|
|
&req.from_community_name,
|
|
community_id,
|
|
req.message.as_deref(),
|
|
)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(json!({"id": request_id})))
|
|
}
|
|
|
|
// ============================================================================
|
|
// Router
|
|
// ============================================================================
|
|
|
|
pub fn router(pool: PgPool) -> Router {
|
|
Router::new()
|
|
// Instances
|
|
.route(
|
|
"/api/federation/instances",
|
|
get(get_instances).post(register_instance),
|
|
)
|
|
.route(
|
|
"/api/federation/instances/{instance_id}/trust",
|
|
post(set_trust_level),
|
|
)
|
|
// Community federations
|
|
.route(
|
|
"/api/communities/{community_id}/federation",
|
|
get(get_community_federations).post(request_federation),
|
|
)
|
|
.route(
|
|
"/api/communities/{community_id}/federation/stats",
|
|
get(get_stats),
|
|
)
|
|
.route(
|
|
"/api/communities/{community_id}/federation/request",
|
|
post(receive_federation_request),
|
|
)
|
|
.route(
|
|
"/api/federation/{federation_id}/approve",
|
|
post(approve_federation),
|
|
)
|
|
.with_state(pool)
|
|
}
|