Compare commits
28 Commits
ae6daea227
...
renovate/c
| Author | SHA1 | Date | |
|---|---|---|---|
| 82f80f8ef4 | |||
| de527d2833 | |||
| e5ca2f98fd | |||
| a51edd6093 | |||
| f544d1d4ca | |||
| e950517ab2 | |||
| 1914d1a429 | |||
| ea4422701d | |||
| 32bbe52cc5 | |||
| 1cff950f8f | |||
| a2bdb7e6b8 | |||
| 3dedd47b14 | |||
| ba31e19c76 | |||
| 6557f4ad45 | |||
| 18206af6b8 | |||
| 280388d11f | |||
| 7455e8771b | |||
| c87dbc670d | |||
| a5ad5973b7 | |||
| 01b1434e37 | |||
| 7a60460973 | |||
| a8cfdaf287 | |||
| f13fac582b | |||
| 4d9909fe80 | |||
| 13d20ff4fb | |||
| 51d14df6bb | |||
| 9c0af4d7d3 | |||
| 4044a99f3f |
768
matrixgw_backend/Cargo.lock
generated
768
matrixgw_backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,10 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
env_logger = "0.11.9"
|
env_logger = "0.11.9"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
clap = { version = "4.5.60", features = ["derive", "env"] }
|
clap = { version = "4.6.0", features = ["derive", "env"] }
|
||||||
anyhow = "1.0.102"
|
anyhow = "1.0.102"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
tokio = { version = "1.49.0", features = ["full"] }
|
tokio = { version = "1.50.0", features = ["full"] }
|
||||||
actix-web = "4.13.0"
|
actix-web = "4.13.0"
|
||||||
actix-session = { version = "0.11.0", features = ["redis-session"] }
|
actix-session = { version = "0.11.0", features = ["redis-session"] }
|
||||||
actix-remote-ip = "0.1.0"
|
actix-remote-ip = "0.1.0"
|
||||||
@@ -22,18 +22,19 @@ base16ct = { version = "1.0.0", features = ["alloc"] }
|
|||||||
futures-util = "0.3.32"
|
futures-util = "0.3.32"
|
||||||
jwt-simple = { version = "0.12.14", default-features = false, features = ["pure-rust"] }
|
jwt-simple = { version = "0.12.14", default-features = false, features = ["pure-rust"] }
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
uuid = { version = "1.21.0", features = ["v4", "serde"] }
|
uuid = { version = "1.22.0", features = ["v4", "serde"] }
|
||||||
ipnet = { version = "2.11.0", features = ["serde"] }
|
ipnet = { version = "2.12.0", features = ["serde"] }
|
||||||
rand = "0.10.0"
|
rand = "0.10.0"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
mailchecker = "6.0.19"
|
mailchecker = "6.0.20"
|
||||||
matrix-sdk = { version = "0.16.0", features = ["e2e-encryption"] }
|
matrix-sdk = { version = "0.16.0", features = ["e2e-encryption"] }
|
||||||
matrix-sdk-ui = "0.16.0"
|
matrix-sdk-ui = "0.16.0"
|
||||||
url = "2.5.8"
|
url = "2.5.8"
|
||||||
ractor = "0.15.10"
|
ractor = "0.15.12"
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
lazy-regex = "3.6.0"
|
lazy-regex = "3.6.0"
|
||||||
actix-ws = "0.4.0"
|
actix-ws = "0.4.0"
|
||||||
infer = "0.19.0"
|
infer = "0.19.0"
|
||||||
rust-embed = "8.11.0"
|
rust-embed = "8.11.0"
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
|
image = "0.25.10"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use crate::controllers::HttpResult;
|
||||||
|
use crate::extractors::matrix_client_extractor::MatrixClientExtractor;
|
||||||
|
use actix_web::HttpResponse;
|
||||||
|
|
||||||
|
/// Get the list of devices for the account
|
||||||
|
pub async fn get_list(client: MatrixClientExtractor) -> HttpResult {
|
||||||
|
let devices = client.client.client.devices().await?.devices;
|
||||||
|
Ok(HttpResponse::Ok().json(devices))
|
||||||
|
}
|
||||||
@@ -4,9 +4,12 @@ use crate::utils::crypt_utils::sha512;
|
|||||||
use actix_web::dev::Payload;
|
use actix_web::dev::Payload;
|
||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
use actix_web::{FromRequest, HttpRequest, HttpResponse, web};
|
use actix_web::{FromRequest, HttpRequest, HttpResponse, web};
|
||||||
|
use image::imageops::FilterType;
|
||||||
|
use image::{ImageFormat, ImageReader};
|
||||||
use matrix_sdk::media::{MediaFormat, MediaRequestParameters, MediaThumbnailSettings};
|
use matrix_sdk::media::{MediaFormat, MediaRequestParameters, MediaThumbnailSettings};
|
||||||
use matrix_sdk::ruma::events::room::MediaSource;
|
use matrix_sdk::ruma::events::room::MediaSource;
|
||||||
use matrix_sdk::ruma::{OwnedMxcUri, UInt};
|
use matrix_sdk::ruma::{OwnedMxcUri, UInt};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct MediaMXCInPath {
|
pub struct MediaMXCInPath {
|
||||||
@@ -29,11 +32,44 @@ pub async fn serve_mxc_file(req: HttpRequest, media: OwnedMxcUri) -> HttpResult
|
|||||||
serve_media(req, MediaSource::Plain(media), query.thumbnail).await
|
serve_media(req, MediaSource::Plain(media), query.thumbnail).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, Default, Copy, Clone)]
|
||||||
|
#[serde(rename_all(deserialize = "lowercase"))]
|
||||||
|
enum ConvertFormat {
|
||||||
|
#[default]
|
||||||
|
Png,
|
||||||
|
Jpeg,
|
||||||
|
Gif,
|
||||||
|
Bmp,
|
||||||
|
Tga,
|
||||||
|
Tiff,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct ServeMediaQuery {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
max_width: Option<u32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
max_height: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
grayscale: bool,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
format: Option<ConvertFormat>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServeMediaQuery {
|
||||||
|
pub fn operation_needed(&self) -> bool {
|
||||||
|
self.max_width.is_some()
|
||||||
|
|| self.max_height.is_some()
|
||||||
|
|| self.format.is_some()
|
||||||
|
|| self.grayscale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Serve a media file
|
/// Serve a media file
|
||||||
pub async fn serve_media(req: HttpRequest, source: MediaSource, thumbnail: bool) -> HttpResult {
|
pub async fn serve_media(req: HttpRequest, source: MediaSource, thumbnail: bool) -> HttpResult {
|
||||||
let client = MatrixClientExtractor::from_request(&req, &mut Payload::None).await?;
|
let client = MatrixClientExtractor::from_request(&req, &mut Payload::None).await?;
|
||||||
|
|
||||||
let media = client
|
let mut media = client
|
||||||
.client
|
.client
|
||||||
.client
|
.client
|
||||||
.media()
|
.media()
|
||||||
@@ -52,11 +88,44 @@ pub async fn serve_media(req: HttpRequest, source: MediaSource, thumbnail: bool)
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let digest = sha512(&media);
|
|
||||||
|
|
||||||
let mime_type = infer::get(&media).map(|x| x.mime_type());
|
let mime_type = infer::get(&media).map(|x| x.mime_type());
|
||||||
|
|
||||||
|
// Check if the media needs to be converted before being returned
|
||||||
|
let query = web::Query::<ServeMediaQuery>::from_request(&req, &mut Payload::None).await?;
|
||||||
|
if query.operation_needed() {
|
||||||
|
let mut img = ImageReader::new(Cursor::new(&media))
|
||||||
|
.with_guessed_format()?
|
||||||
|
.decode()?;
|
||||||
|
|
||||||
|
// Resize image if required
|
||||||
|
if query.max_width.is_some() || query.max_height.is_some() {
|
||||||
|
img = img.resize(
|
||||||
|
query.max_width.unwrap_or(img.width()),
|
||||||
|
query.max_height.unwrap_or(img.height()),
|
||||||
|
FilterType::Lanczos3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply grayscal if needed
|
||||||
|
if query.grayscale {
|
||||||
|
img = img.grayscale();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize new image
|
||||||
|
let format = match query.format.unwrap_or_default() {
|
||||||
|
ConvertFormat::Png => ImageFormat::Png,
|
||||||
|
ConvertFormat::Jpeg => ImageFormat::Jpeg,
|
||||||
|
ConvertFormat::Gif => ImageFormat::Gif,
|
||||||
|
ConvertFormat::Bmp => ImageFormat::Bmp,
|
||||||
|
ConvertFormat::Tga => ImageFormat::Tga,
|
||||||
|
ConvertFormat::Tiff => ImageFormat::Tiff,
|
||||||
|
};
|
||||||
|
media.clear();
|
||||||
|
img.write_to(&mut Cursor::new(&mut media), format)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the browser already knows the etag
|
// Check if the browser already knows the etag
|
||||||
|
let digest = sha512(&media);
|
||||||
if let Some(c) = req.headers().get(header::IF_NONE_MATCH)
|
if let Some(c) = req.headers().get(header::IF_NONE_MATCH)
|
||||||
&& c.to_str().unwrap_or("") == digest
|
&& c.to_str().unwrap_or("") == digest
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -115,17 +115,31 @@ pub struct RoomIdInPath {
|
|||||||
pub(crate) room_id: OwnedRoomId,
|
pub(crate) room_id: OwnedRoomId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct SingleRoomQuery {
|
||||||
|
#[serde(default)]
|
||||||
|
pub with_latest_event: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the list of joined rooms of the user
|
/// Get the list of joined rooms of the user
|
||||||
pub async fn single_room_info(
|
pub async fn single_room_info(
|
||||||
client: MatrixClientExtractor,
|
client: MatrixClientExtractor,
|
||||||
path: web::Path<RoomIdInPath>,
|
path: web::Path<RoomIdInPath>,
|
||||||
|
query: web::Query<SingleRoomQuery>,
|
||||||
) -> HttpResult {
|
) -> HttpResult {
|
||||||
let notifs = client.client.client.notification_settings().await;
|
let notifs = client.client.client.notification_settings().await;
|
||||||
|
|
||||||
Ok(match client.client.client.get_room(&path.room_id) {
|
let Some(room) = client.client.client.get_room(&path.room_id) else {
|
||||||
None => HttpResponse::NotFound().json("Room not found"),
|
return Ok(HttpResponse::NotFound().json("Room not found"));
|
||||||
Some(r) => HttpResponse::Ok().json(APIRoomInfo::from_room(&r, ¬ifs).await?),
|
};
|
||||||
})
|
|
||||||
|
let mut room_info = APIRoomInfo::from_room(&room, ¬ifs).await?;
|
||||||
|
|
||||||
|
if !query.with_latest_event {
|
||||||
|
room_info.latest_event = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(room_info))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get room avatar
|
/// Get room avatar
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod matrix_devices_controller;
|
||||||
pub mod matrix_event_controller;
|
pub mod matrix_event_controller;
|
||||||
pub mod matrix_media_controller;
|
pub mod matrix_media_controller;
|
||||||
pub mod matrix_profile_controller;
|
pub mod matrix_profile_controller;
|
||||||
|
|||||||
@@ -25,12 +25,16 @@ pub enum HttpFailure {
|
|||||||
ActixError(#[from] actix_web::Error),
|
ActixError(#[from] actix_web::Error),
|
||||||
#[error("Matrix error: {0}")]
|
#[error("Matrix error: {0}")]
|
||||||
MatrixError(#[from] matrix_sdk::Error),
|
MatrixError(#[from] matrix_sdk::Error),
|
||||||
|
#[error("Matrix HTTP error: {0}")]
|
||||||
|
MatrixHTTPError(#[from] matrix_sdk::HttpError),
|
||||||
#[error("Matrix decryptor error: {0}")]
|
#[error("Matrix decryptor error: {0}")]
|
||||||
MatrixDecryptorError(#[from] matrix_sdk::encryption::DecryptorError),
|
MatrixDecryptorError(#[from] matrix_sdk::encryption::DecryptorError),
|
||||||
#[error("Serde JSON error: {0}")]
|
#[error("Serde JSON error: {0}")]
|
||||||
SerdeJSON(#[from] serde_json::Error),
|
SerdeJSON(#[from] serde_json::Error),
|
||||||
#[error("Standard library error: {0}")]
|
#[error("Standard library error: {0}")]
|
||||||
StdLibError(#[from] std::io::Error),
|
StdLibError(#[from] std::io::Error),
|
||||||
|
#[error("Image error: {0}")]
|
||||||
|
ImageDecode(#[from] image::ImageError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for HttpFailure {
|
impl ResponseError for HttpFailure {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use matrixgw_backend::app_config::AppConfig;
|
|||||||
use matrixgw_backend::broadcast_messages::BroadcastMessage;
|
use matrixgw_backend::broadcast_messages::BroadcastMessage;
|
||||||
use matrixgw_backend::constants;
|
use matrixgw_backend::constants;
|
||||||
use matrixgw_backend::controllers::matrix::{
|
use matrixgw_backend::controllers::matrix::{
|
||||||
matrix_event_controller, matrix_media_controller, matrix_profile_controller,
|
matrix_devices_controller, matrix_event_controller, matrix_media_controller,
|
||||||
matrix_room_controller, matrix_space_controller,
|
matrix_profile_controller, matrix_room_controller, matrix_space_controller,
|
||||||
};
|
};
|
||||||
use matrixgw_backend::controllers::server_controller::ServerConstraints;
|
use matrixgw_backend::controllers::server_controller::ServerConstraints;
|
||||||
use matrixgw_backend::controllers::{
|
use matrixgw_backend::controllers::{
|
||||||
@@ -142,6 +142,11 @@ async fn main() -> std::io::Result<()> {
|
|||||||
web::get().to(matrix_sync_thread_controller::status),
|
web::get().to(matrix_sync_thread_controller::status),
|
||||||
)
|
)
|
||||||
.service(web::resource("/api/ws").route(web::get().to(ws_controller::ws)))
|
.service(web::resource("/api/ws").route(web::get().to(ws_controller::ws)))
|
||||||
|
// Matrix connection status
|
||||||
|
.route(
|
||||||
|
"/api/matrix/devices",
|
||||||
|
web::get().to(matrix_devices_controller::get_list),
|
||||||
|
)
|
||||||
// Matrix spaces controller
|
// Matrix spaces controller
|
||||||
.route(
|
.route(
|
||||||
"/api/matrix/space/hierarchy",
|
"/api/matrix/space/hierarchy",
|
||||||
|
|||||||
806
matrixgw_frontend/package-lock.json
generated
806
matrixgw_frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,12 +13,12 @@
|
|||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@fontsource/roboto": "^5.2.10",
|
"@fontsource/roboto": "^5.2.10",
|
||||||
"@mui/icons-material": "^7.3.8",
|
"@mui/icons-material": "^7.3.9",
|
||||||
"@mui/material": "^7.3.8",
|
"@mui/material": "^7.3.9",
|
||||||
"@mui/x-data-grid": "^8.27.3",
|
"@mui/x-data-grid": "^8.27.5",
|
||||||
"@mui/x-date-pickers": "^8.27.2",
|
"@mui/x-date-pickers": "^8.27.2",
|
||||||
"date-and-time": "^4.3.0",
|
"date-and-time": "^4.3.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.20",
|
||||||
"emoji-picker-react": "^4.18.0",
|
"emoji-picker-react": "^4.18.0",
|
||||||
"filesize": "^11.0.13",
|
"filesize": "^11.0.13",
|
||||||
"is-cidr": "^6.0.3",
|
"is-cidr": "^6.0.3",
|
||||||
@@ -31,16 +31,16 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@types/node": "^25.3.3",
|
"@types/node": "^25.5.0",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react": "^5.1.4",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"eslint": "^9.39.3",
|
"eslint": "^10.0.3",
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.5.2",
|
"eslint-plugin-react-refresh": "^0.5.2",
|
||||||
"globals": "^17.4.0",
|
"globals": "^17.4.0",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~5.9.3",
|
||||||
"typescript-eslint": "^8.56.1",
|
"typescript-eslint": "^8.57.1",
|
||||||
"vite": "npm:rolldown-vite@7.3.1"
|
"vite": "npm:rolldown-vite@7.3.1"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|||||||
Reference in New Issue
Block a user