mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Implement live syncback feature and enhance API for service data transfer
- Added a new `performSyncback` function in the `App` module to handle the synchronization of service data from the game to the server. - Introduced a `SyncbackConfirm` UI component to confirm syncback actions from the user. - Updated the API to include a new `/api/syncback` endpoint for handling syncback requests, validating protocol and server versions. - Enhanced the `SyncbackSignal` mechanism to facilitate communication between the server and the syncback process. - Added tests to ensure the functionality of the live syncback feature and its integration with the CLI. These changes improve the overall synchronization capabilities of the application, allowing for real-time updates and better project management.
This commit is contained in:
parent
6f8e5a5cf2
commit
307ac81aec
17 changed files with 1411 additions and 97 deletions
|
|
@ -29,8 +29,9 @@ use crate::{
|
|||
web::{
|
||||
interface::{
|
||||
ErrorResponse, Instance, InstanceMetadata, MessagesPacket, OpenResponse, ReadResponse,
|
||||
ServerInfoResponse, SocketPacket, SocketPacketBody, SocketPacketType, SubscribeMessage,
|
||||
WriteRequest, WriteResponse, PROTOCOL_VERSION, SERVER_VERSION,
|
||||
ServerInfoResponse, SocketPacket, SocketPacketBody, SocketPacketType,
|
||||
SubscribeMessage, SyncbackPayload, SyncbackRequest, WriteRequest, WriteResponse,
|
||||
PROTOCOL_VERSION, SERVER_VERSION,
|
||||
},
|
||||
util::{deserialize_msgpack, msgpack, msgpack_ok, serialize_msgpack},
|
||||
},
|
||||
|
|
@ -122,6 +123,7 @@ fn variant_to_json(variant: &Variant) -> Option<serde_json::Value> {
|
|||
pub async fn call(
|
||||
serve_session: Arc<ServeSession>,
|
||||
mut request: Request<Incoming>,
|
||||
syncback_signal: Arc<super::SyncbackSignal>,
|
||||
) -> Response<Full<Bytes>> {
|
||||
let service = ApiService::new(serve_session);
|
||||
|
||||
|
|
@ -153,6 +155,9 @@ pub async fn call(
|
|||
service.handle_api_open(request).await
|
||||
}
|
||||
(&Method::POST, "/api/write") => service.handle_api_write(request).await,
|
||||
(&Method::POST, "/api/syncback") => {
|
||||
handle_api_syncback(request, &service, syncback_signal).await
|
||||
}
|
||||
(&Method::GET, "/api/validate-tree") => service.handle_api_validate_tree().await,
|
||||
|
||||
(_method, path) => msgpack(
|
||||
|
|
@ -162,6 +167,96 @@ pub async fn call(
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_api_syncback(
|
||||
request: Request<Incoming>,
|
||||
service: &ApiService,
|
||||
syncback_signal: Arc<super::SyncbackSignal>,
|
||||
) -> Response<Full<Bytes>> {
|
||||
let body = match request.into_body().collect().await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(err) => {
|
||||
return msgpack(
|
||||
ErrorResponse::bad_request(format!("Failed to read request body: {err}")),
|
||||
StatusCode::BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let syncback_request: SyncbackRequest = match deserialize_msgpack(&body) {
|
||||
Ok(req) => req,
|
||||
Err(err) => {
|
||||
return msgpack(
|
||||
ErrorResponse::bad_request(format!(
|
||||
"Failed to deserialize syncback request: {err}"
|
||||
)),
|
||||
StatusCode::BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if syncback_request.protocol_version != PROTOCOL_VERSION {
|
||||
return msgpack(
|
||||
ErrorResponse::bad_request(format!(
|
||||
"Protocol version mismatch: expected {}, got {}",
|
||||
PROTOCOL_VERSION, syncback_request.protocol_version
|
||||
)),
|
||||
StatusCode::BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
let server_semver = SERVER_VERSION;
|
||||
let server_parts: Vec<&str> = server_semver.split('.').collect();
|
||||
let client_parts: Vec<&str> = syncback_request.server_version.split('.').collect();
|
||||
let server_major_minor = server_parts.get(..2);
|
||||
let client_major_minor = client_parts.get(..2);
|
||||
if server_major_minor != client_major_minor {
|
||||
return msgpack(
|
||||
ErrorResponse::bad_request(format!(
|
||||
"Server version mismatch: server is {}, plugin expects {}",
|
||||
SERVER_VERSION, syncback_request.server_version
|
||||
)),
|
||||
StatusCode::BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(place_id) = syncback_request.place_id {
|
||||
if let Some(expected) = service.serve_session.serve_place_ids() {
|
||||
if !expected.contains(&place_id) {
|
||||
return msgpack(
|
||||
ErrorResponse::bad_request(format!(
|
||||
"Place ID {} is not in the servePlaceIds whitelist",
|
||||
place_id
|
||||
)),
|
||||
StatusCode::FORBIDDEN,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(blocked) = service.serve_session.blocked_place_ids() {
|
||||
if blocked.contains(&place_id) {
|
||||
return msgpack(
|
||||
ErrorResponse::bad_request(format!(
|
||||
"Place ID {} is in the blockedPlaceIds list",
|
||||
place_id
|
||||
)),
|
||||
StatusCode::FORBIDDEN,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"Live syncback requested with {} service chunks",
|
||||
syncback_request.services.len()
|
||||
);
|
||||
|
||||
let payload = SyncbackPayload {
|
||||
services: syncback_request.services,
|
||||
};
|
||||
syncback_signal.fire(payload);
|
||||
|
||||
msgpack_ok(&serde_json::json!({"status": "syncback_initiated"}))
|
||||
}
|
||||
|
||||
pub struct ApiService {
|
||||
serve_session: Arc<ServeSession>,
|
||||
suppressed_paths: Arc<Mutex<HashMap<PathBuf, (usize, usize)>>>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue