From 633bf6ce784333c3ca8a66e0f0866b6bc8bbabdc Mon Sep 17 00:00:00 2001 From: Filipriec Date: Mon, 31 Aug 2026 16:26:20 +0200 Subject: [PATCH] dumping and restoration of db --- client-gui2 | 2 +- common/proto/backup.proto | 18 +++ common/src/proto/descriptor.bin | Bin 211624 -> 213228 bytes common/src/proto/komp_ac.backup.rs | 175 +++++++++++++++++++++++++++++ server | 2 +- 5 files changed, 195 insertions(+), 2 deletions(-) diff --git a/client-gui2 b/client-gui2 index 412a577e..45677dc5 160000 --- a/client-gui2 +++ b/client-gui2 @@ -1 +1 @@ -Subproject commit 412a577e47d64c1b220aef061da983268ee48dc6 +Subproject commit 45677dc55a11b1889663c2566239c44df951b694 diff --git a/common/proto/backup.proto b/common/proto/backup.proto index 9673ba2d..9d8c0651 100644 --- a/common/proto/backup.proto +++ b/common/proto/backup.proto @@ -12,6 +12,8 @@ service BackupService { rpc ListDatabases(komp_ac.common.Empty) returns (ListDatabasesResponse); rpc CreateDatabase(CreateDatabaseRequest) returns (DatabaseInfo); rpc RunDatabaseMigrations(DatabaseRequest) returns (DatabaseInfo); + rpc ListDatabaseDumps(komp_ac.common.Empty) returns (ListDatabaseDumpsResponse); + rpc RestoreDatabase(RestoreDatabaseRequest) returns (DatabaseInfo); } message DatabaseRequest { @@ -22,6 +24,21 @@ message CreateDatabaseRequest { string database_name = 1; } +message RestoreDatabaseRequest { + string database_name = 1; + string dump_file_name = 2; +} + +message DatabaseDumpInfo { + string file_name = 1; + int64 size_bytes = 2; + int64 modified_at_unix_seconds = 3; +} + +message ListDatabaseDumpsResponse { + repeated DatabaseDumpInfo dumps = 1; +} + message DatabaseInfo { string database_name = 1; bool active = 2; @@ -32,6 +49,7 @@ message DatabaseInfo { bool migration_dirty = 7; bool application_database = 8; string status = 9; + bool empty = 10; } message ListDatabasesResponse { diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index b1d04d2a77acbb505a3aeb2fde072fc103deb384..1970654a64d5c03a9834a63b523bf09f65ecb89c 100644 GIT binary patch delta 5665 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 CN_mX{ delta 4183 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 G%YOs#?HY&x diff --git a/common/src/proto/komp_ac.backup.rs b/common/src/proto/komp_ac.backup.rs index dab16035..16f36faf 100644 --- a/common/src/proto/komp_ac.backup.rs +++ b/common/src/proto/komp_ac.backup.rs @@ -10,6 +10,27 @@ pub struct CreateDatabaseRequest { pub database_name: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RestoreDatabaseRequest { + #[prost(string, tag = "1")] + pub database_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub dump_file_name: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct DatabaseDumpInfo { + #[prost(string, tag = "1")] + pub file_name: ::prost::alloc::string::String, + #[prost(int64, tag = "2")] + pub size_bytes: i64, + #[prost(int64, tag = "3")] + pub modified_at_unix_seconds: i64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListDatabaseDumpsResponse { + #[prost(message, repeated, tag = "1")] + pub dumps: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DatabaseInfo { #[prost(string, tag = "1")] pub database_name: ::prost::alloc::string::String, @@ -29,6 +50,8 @@ pub struct DatabaseInfo { pub application_database: bool, #[prost(string, tag = "9")] pub status: ::prost::alloc::string::String, + #[prost(bool, tag = "10")] + pub empty: bool, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ListDatabasesResponse { @@ -475,6 +498,55 @@ pub mod backup_service_client { ); self.inner.unary(req, path, codec).await } + pub async fn list_database_dumps( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/komp_ac.backup.BackupService/ListDatabaseDumps", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("komp_ac.backup.BackupService", "ListDatabaseDumps"), + ); + self.inner.unary(req, path, codec).await + } + pub async fn restore_database( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/komp_ac.backup.BackupService/RestoreDatabase", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("komp_ac.backup.BackupService", "RestoreDatabase"), + ); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -540,6 +612,17 @@ pub mod backup_service_server { &self, request: tonic::Request, ) -> std::result::Result, tonic::Status>; + async fn list_database_dumps( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn restore_database( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; } #[derive(Debug)] pub struct BackupServiceServer { @@ -982,6 +1065,98 @@ pub mod backup_service_server { }; Box::pin(fut) } + "/komp_ac.backup.BackupService/ListDatabaseDumps" => { + #[allow(non_camel_case_types)] + struct ListDatabaseDumpsSvc(pub Arc); + impl< + T: BackupService, + > tonic::server::UnaryService + for ListDatabaseDumpsSvc { + type Response = super::ListDatabaseDumpsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_database_dumps(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = ListDatabaseDumpsSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/komp_ac.backup.BackupService/RestoreDatabase" => { + #[allow(non_camel_case_types)] + struct RestoreDatabaseSvc(pub Arc); + impl< + T: BackupService, + > tonic::server::UnaryService + for RestoreDatabaseSvc { + type Response = super::DatabaseInfo; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::restore_database(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = RestoreDatabaseSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { let mut response = http::Response::new( diff --git a/server b/server index 8f18fa39..b8ca18a6 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 8f18fa39e342626c10b08e7409a1a6909c383f55 +Subproject commit b8ca18a627297bb6c2b3a3207b32c116a00de08a