mirror of
				https://github.com/MindWorkAI/AI-Studio.git
				synced 2025-11-04 03:40:21 +00:00 
			
		
		
		
	Refactored .NET server
This commit is contained in:
		
							parent
							
								
									a744c1e16e
								
							
						
					
					
						commit
						90e037b59b
					
				
							
								
								
									
										170
									
								
								runtime/src/dotnet.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								runtime/src/dotnet.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,170 @@
 | 
				
			|||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use std::sync::{Arc, Mutex};
 | 
				
			||||||
 | 
					use base64::Engine;
 | 
				
			||||||
 | 
					use base64::prelude::BASE64_STANDARD;
 | 
				
			||||||
 | 
					use log::{debug, error, info, warn};
 | 
				
			||||||
 | 
					use once_cell::sync::Lazy;
 | 
				
			||||||
 | 
					use rocket::get;
 | 
				
			||||||
 | 
					use tauri::api::process::{Command, CommandChild, CommandEvent};
 | 
				
			||||||
 | 
					use tauri::Url;
 | 
				
			||||||
 | 
					use crate::api_token::{APIToken, API_TOKEN};
 | 
				
			||||||
 | 
					use crate::app_window::change_location_to;
 | 
				
			||||||
 | 
					use crate::encryption::ENCRYPTION;
 | 
				
			||||||
 | 
					use crate::environment::is_dev;
 | 
				
			||||||
 | 
					use crate::network::get_available_port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The .NET server is started in a separate process and communicates with this
 | 
				
			||||||
 | 
					// runtime process via IPC. However, we do net start the .NET server in
 | 
				
			||||||
 | 
					// the development environment.
 | 
				
			||||||
 | 
					static DOTNET_SERVER: Lazy<Arc<Mutex<Option<CommandChild>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The .NET server port is relevant for the production environment only, sine we
 | 
				
			||||||
 | 
					// do not start the server in the development environment.
 | 
				
			||||||
 | 
					static DOTNET_SERVER_PORT: Lazy<u16> = Lazy::new(|| get_available_port().unwrap());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static DOTNET_INITIALIZED: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[get("/system/dotnet/port")]
 | 
				
			||||||
 | 
					pub fn dotnet_port(_token: APIToken) -> String {
 | 
				
			||||||
 | 
					    let dotnet_server_port = *DOTNET_SERVER_PORT;
 | 
				
			||||||
 | 
					    format!("{dotnet_server_port}")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn start_dotnet_server(api_server_port: u16, certificate_fingerprint: String) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Get the secret password & salt and convert it to a base64 string:
 | 
				
			||||||
 | 
					    let secret_password = BASE64_STANDARD.encode(ENCRYPTION.secret_password);
 | 
				
			||||||
 | 
					    let secret_key_salt = BASE64_STANDARD.encode(ENCRYPTION.secret_key_salt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let dotnet_server_environment = HashMap::from_iter([
 | 
				
			||||||
 | 
					        (String::from("AI_STUDIO_SECRET_PASSWORD"), secret_password),
 | 
				
			||||||
 | 
					        (String::from("AI_STUDIO_SECRET_KEY_SALT"), secret_key_salt),
 | 
				
			||||||
 | 
					        (String::from("AI_STUDIO_CERTIFICATE_FINGERPRINT"), certificate_fingerprint),
 | 
				
			||||||
 | 
					        (String::from("AI_STUDIO_API_TOKEN"), API_TOKEN.to_hex_text().to_string()),
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    info!("Try to start the .NET server...");
 | 
				
			||||||
 | 
					    let server_spawn_clone = DOTNET_SERVER.clone();
 | 
				
			||||||
 | 
					    tauri::async_runtime::spawn(async move {
 | 
				
			||||||
 | 
					        let api_port = api_server_port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (mut rx, child) = match is_dev() {
 | 
				
			||||||
 | 
					            true => {
 | 
				
			||||||
 | 
					                // We are in the development environment, so we try to start a process
 | 
				
			||||||
 | 
					                // with `dotnet run` in the `../app/MindWork AI Studio` directory. But
 | 
				
			||||||
 | 
					                // we cannot issue a sidecar because we cannot use any command for the
 | 
				
			||||||
 | 
					                // sidecar (see Tauri configuration). Thus, we use a standard Rust process:
 | 
				
			||||||
 | 
					                warn!(Source = "Bootloader .NET"; "Development environment detected; start .NET server using 'dotnet run'.");
 | 
				
			||||||
 | 
					                Command::new("dotnet")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Start the .NET server in the `../app/MindWork AI Studio` directory.
 | 
				
			||||||
 | 
					                    // We provide the runtime API server port to the .NET server:
 | 
				
			||||||
 | 
					                    .args(["run", "--project", "../app/MindWork AI Studio", "--", format!("{api_port}").as_str()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    .envs(dotnet_server_environment)
 | 
				
			||||||
 | 
					                    .spawn()
 | 
				
			||||||
 | 
					                    .expect("Failed to spawn .NET server process.")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            false => {
 | 
				
			||||||
 | 
					                Command::new_sidecar("mindworkAIStudioServer")
 | 
				
			||||||
 | 
					                    .expect("Failed to create sidecar")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Provide the runtime API server port to the .NET server:
 | 
				
			||||||
 | 
					                    .args([format!("{api_port}").as_str()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    .envs(dotnet_server_environment)
 | 
				
			||||||
 | 
					                    .spawn()
 | 
				
			||||||
 | 
					                    .expect("Failed to spawn .NET server process.")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let server_pid = child.pid();
 | 
				
			||||||
 | 
					        info!(Source = "Bootloader .NET"; "The .NET server process started with PID={server_pid}.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Save the server process to stop it later:
 | 
				
			||||||
 | 
					        *server_spawn_clone.lock().unwrap() = Some(child);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Log the output of the .NET server:
 | 
				
			||||||
 | 
					        while let Some(CommandEvent::Stdout(line)) = rx.recv().await {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Remove newline characters from the end:
 | 
				
			||||||
 | 
					            let line = line.trim_end();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Starts the line with '=>'?
 | 
				
			||||||
 | 
					            if line.starts_with("=>") {
 | 
				
			||||||
 | 
					                // Yes. This means that the line is a log message from the .NET server.
 | 
				
			||||||
 | 
					                // The format is: '<YYYY-MM-dd HH:mm:ss.fff> [<log level>] <source>: <message>'.
 | 
				
			||||||
 | 
					                // We try to parse this line and log it with the correct log level:
 | 
				
			||||||
 | 
					                let line = line.trim_start_matches("=>").trim();
 | 
				
			||||||
 | 
					                let parts = line.split_once(": ").unwrap();
 | 
				
			||||||
 | 
					                let left_part = parts.0.trim();
 | 
				
			||||||
 | 
					                let message = parts.1.trim();
 | 
				
			||||||
 | 
					                let parts = left_part.split_once("] ").unwrap();
 | 
				
			||||||
 | 
					                let level = parts.0.split_once("[").unwrap().1.trim();
 | 
				
			||||||
 | 
					                let source = parts.1.trim();
 | 
				
			||||||
 | 
					                match level {
 | 
				
			||||||
 | 
					                    "Trace" => debug!(Source = ".NET Server", Comp = source; "{message}"),
 | 
				
			||||||
 | 
					                    "Debug" => debug!(Source = ".NET Server", Comp = source; "{message}"),
 | 
				
			||||||
 | 
					                    "Information" => info!(Source = ".NET Server", Comp = source; "{message}"),
 | 
				
			||||||
 | 
					                    "Warning" => warn!(Source = ".NET Server", Comp = source; "{message}"),
 | 
				
			||||||
 | 
					                    "Error" => error!(Source = ".NET Server", Comp = source; "{message}"),
 | 
				
			||||||
 | 
					                    "Critical" => error!(Source = ".NET Server", Comp = source; "{message}"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    _ => error!(Source = ".NET Server", Comp = source; "{message} (unknown log level '{level}')"),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                let lower_line = line.to_lowercase();
 | 
				
			||||||
 | 
					                if lower_line.contains("error") {
 | 
				
			||||||
 | 
					                    error!(Source = ".NET Server"; "{line}");
 | 
				
			||||||
 | 
					                } else if lower_line.contains("warning") {
 | 
				
			||||||
 | 
					                    warn!(Source = ".NET Server"; "{line}");
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    info!(Source = ".NET Server"; "{line}");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[get("/system/dotnet/ready")]
 | 
				
			||||||
 | 
					pub async fn dotnet_ready(_token: APIToken) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We create a manual scope for the lock to be released as soon as possible.
 | 
				
			||||||
 | 
					    // This is necessary because we cannot await any function while the lock is
 | 
				
			||||||
 | 
					    // held.
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let mut initialized = DOTNET_INITIALIZED.lock().unwrap();
 | 
				
			||||||
 | 
					        if *initialized {
 | 
				
			||||||
 | 
					            error!("Anyone tried to initialize the runtime twice. This is not intended.");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        info!("The .NET server was booted successfully.");
 | 
				
			||||||
 | 
					        *initialized = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let dotnet_server_port = *DOTNET_SERVER_PORT;
 | 
				
			||||||
 | 
					    let url = match Url::parse(format!("http://localhost:{dotnet_server_port}").as_str())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Ok(url) => url,
 | 
				
			||||||
 | 
					        Err(msg) => {
 | 
				
			||||||
 | 
					            error!("Error while parsing URL for navigating to the app: {msg}");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    change_location_to(url.as_str()).await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn stop_dotnet_server() {
 | 
				
			||||||
 | 
					    if let Some(server_process) = DOTNET_SERVER.lock().unwrap().take() {
 | 
				
			||||||
 | 
					        let server_kill_result = server_process.kill();
 | 
				
			||||||
 | 
					        match server_kill_result {
 | 
				
			||||||
 | 
					            Ok(_) => info!("The .NET server process was stopped."),
 | 
				
			||||||
 | 
					            Err(e) => error!("Failed to stop the .NET server process: {e}."),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        warn!("The .NET server process was not started or is already stopped.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
pub mod encryption;
 | 
					pub mod encryption;
 | 
				
			||||||
pub mod log;
 | 
					pub mod log;
 | 
				
			||||||
pub mod environment;
 | 
					pub mod environment;
 | 
				
			||||||
 | 
					pub mod dotnet;
 | 
				
			||||||
pub mod network;
 | 
					pub mod network;
 | 
				
			||||||
pub mod api_token;
 | 
					pub mod api_token;
 | 
				
			||||||
@ -36,14 +36,6 @@ use mindwork_ai_studio::environment::{is_dev, is_prod};
 | 
				
			|||||||
use mindwork_ai_studio::log::{init_logging, switch_to_file_logging};
 | 
					use mindwork_ai_studio::log::{init_logging, switch_to_file_logging};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The .NET server is started in a separate process and communicates with this
 | 
					 | 
				
			||||||
// runtime process via IPC. However, we do net start the .NET server in
 | 
					 | 
				
			||||||
// the development environment.
 | 
					 | 
				
			||||||
static DOTNET_SERVER: Lazy<Arc<Mutex<Option<CommandChild>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// The .NET server port is relevant for the production environment only, sine we
 | 
					 | 
				
			||||||
// do not start the server in the development environment.
 | 
					 | 
				
			||||||
static DOTNET_SERVER_PORT: Lazy<u16> = Lazy::new(|| get_available_port().unwrap());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The port used for the runtime API server. In the development environment, we use a fixed
 | 
					// The port used for the runtime API server. In the development environment, we use a fixed
 | 
				
			||||||
// port, in the production environment we use the next available port. This differentiation
 | 
					// port, in the production environment we use the next available port. This differentiation
 | 
				
			||||||
@ -63,9 +55,6 @@ static MAIN_WINDOW: Lazy<Mutex<Option<Window>>> = Lazy::new(|| Mutex::new(None))
 | 
				
			|||||||
// The update response coming from the Tauri updater.
 | 
					// The update response coming from the Tauri updater.
 | 
				
			||||||
static CHECK_UPDATE_RESPONSE: Lazy<Mutex<Option<UpdateResponse<tauri::Wry>>>> = Lazy::new(|| Mutex::new(None));
 | 
					static CHECK_UPDATE_RESPONSE: Lazy<Mutex<Option<UpdateResponse<tauri::Wry>>>> = Lazy::new(|| Mutex::new(None));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
static DOTNET_INITIALIZED: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[tokio::main]
 | 
					#[tokio::main]
 | 
				
			||||||
async fn main() {
 | 
					async fn main() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -178,100 +167,7 @@ async fn main() {
 | 
				
			|||||||
            .launch().await.unwrap();
 | 
					            .launch().await.unwrap();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get the secret password & salt and convert it to a base64 string:
 | 
					 | 
				
			||||||
    let secret_password = BASE64_STANDARD.encode(ENCRYPTION.secret_password);
 | 
					 | 
				
			||||||
    let secret_key_salt = BASE64_STANDARD.encode(ENCRYPTION.secret_key_salt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let dotnet_server_environment = HashMap::from_iter([
 | 
					 | 
				
			||||||
        (String::from("AI_STUDIO_SECRET_PASSWORD"), secret_password),
 | 
					 | 
				
			||||||
        (String::from("AI_STUDIO_SECRET_KEY_SALT"), secret_key_salt),
 | 
					 | 
				
			||||||
        (String::from("AI_STUDIO_CERTIFICATE_FINGERPRINT"), certificate_fingerprint),
 | 
					 | 
				
			||||||
        (String::from("AI_STUDIO_API_TOKEN"), API_TOKEN.to_hex_text().to_string()),
 | 
					 | 
				
			||||||
    ]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    info!("Secret password for the IPC channel was generated successfully.");
 | 
					    info!("Secret password for the IPC channel was generated successfully.");
 | 
				
			||||||
    info!("Try to start the .NET server...");
 | 
					 | 
				
			||||||
    let server_spawn_clone = DOTNET_SERVER.clone();
 | 
					 | 
				
			||||||
    tauri::async_runtime::spawn(async move {
 | 
					 | 
				
			||||||
        let api_port = *API_SERVER_PORT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let (mut rx, child) = match is_dev() {
 | 
					 | 
				
			||||||
            true => {
 | 
					 | 
				
			||||||
                // We are in the development environment, so we try to start a process
 | 
					 | 
				
			||||||
                // with `dotnet run` in the `../app/MindWork AI Studio` directory. But
 | 
					 | 
				
			||||||
                // we cannot issue a sidecar because we cannot use any command for the
 | 
					 | 
				
			||||||
                // sidecar (see Tauri configuration). Thus, we use a standard Rust process:
 | 
					 | 
				
			||||||
                warn!(Source = "Bootloader .NET"; "Development environment detected; start .NET server using 'dotnet run'.");
 | 
					 | 
				
			||||||
                Command::new("dotnet")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Start the .NET server in the `../app/MindWork AI Studio` directory.
 | 
					 | 
				
			||||||
                    // We provide the runtime API server port to the .NET server:
 | 
					 | 
				
			||||||
                    .args(["run", "--project", "../app/MindWork AI Studio", "--", format!("{api_port}").as_str()])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    .envs(dotnet_server_environment)
 | 
					 | 
				
			||||||
                    .spawn()
 | 
					 | 
				
			||||||
                    .expect("Failed to spawn .NET server process.")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            false => {
 | 
					 | 
				
			||||||
                Command::new_sidecar("mindworkAIStudioServer")
 | 
					 | 
				
			||||||
                    .expect("Failed to create sidecar")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Provide the runtime API server port to the .NET server:
 | 
					 | 
				
			||||||
                    .args([format!("{api_port}").as_str()])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    .envs(dotnet_server_environment)
 | 
					 | 
				
			||||||
                    .spawn()
 | 
					 | 
				
			||||||
                    .expect("Failed to spawn .NET server process.")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let server_pid = child.pid();
 | 
					 | 
				
			||||||
        info!(Source = "Bootloader .NET"; "The .NET server process started with PID={server_pid}.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Save the server process to stop it later:
 | 
					 | 
				
			||||||
        *server_spawn_clone.lock().unwrap() = Some(child);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Log the output of the .NET server:
 | 
					 | 
				
			||||||
        while let Some(CommandEvent::Stdout(line)) = rx.recv().await {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Remove newline characters from the end:
 | 
					 | 
				
			||||||
            let line = line.trim_end();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Starts the line with '=>'?
 | 
					 | 
				
			||||||
            if line.starts_with("=>") {
 | 
					 | 
				
			||||||
                // Yes. This means that the line is a log message from the .NET server.
 | 
					 | 
				
			||||||
                // The format is: '<YYYY-MM-dd HH:mm:ss.fff> [<log level>] <source>: <message>'.
 | 
					 | 
				
			||||||
                // We try to parse this line and log it with the correct log level:
 | 
					 | 
				
			||||||
                let line = line.trim_start_matches("=>").trim();
 | 
					 | 
				
			||||||
                let parts = line.split_once(": ").unwrap();
 | 
					 | 
				
			||||||
                let left_part = parts.0.trim();
 | 
					 | 
				
			||||||
                let message = parts.1.trim();
 | 
					 | 
				
			||||||
                let parts = left_part.split_once("] ").unwrap();
 | 
					 | 
				
			||||||
                let level = parts.0.split_once("[").unwrap().1.trim();
 | 
					 | 
				
			||||||
                let source = parts.1.trim();
 | 
					 | 
				
			||||||
                match level {
 | 
					 | 
				
			||||||
                    "Trace" => debug!(Source = ".NET Server", Comp = source; "{message}"),
 | 
					 | 
				
			||||||
                    "Debug" => debug!(Source = ".NET Server", Comp = source; "{message}"),
 | 
					 | 
				
			||||||
                    "Information" => info!(Source = ".NET Server", Comp = source; "{message}"),
 | 
					 | 
				
			||||||
                    "Warning" => warn!(Source = ".NET Server", Comp = source; "{message}"),
 | 
					 | 
				
			||||||
                    "Error" => error!(Source = ".NET Server", Comp = source; "{message}"),
 | 
					 | 
				
			||||||
                    "Critical" => error!(Source = ".NET Server", Comp = source; "{message}"),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    _ => error!(Source = ".NET Server", Comp = source; "{message} (unknown log level '{level}')"),
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                let lower_line = line.to_lowercase();
 | 
					 | 
				
			||||||
                if lower_line.contains("error") {
 | 
					 | 
				
			||||||
                    error!(Source = ".NET Server"; "{line}");
 | 
					 | 
				
			||||||
                } else if lower_line.contains("warning") {
 | 
					 | 
				
			||||||
                    warn!(Source = ".NET Server"; "{line}");
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    info!(Source = ".NET Server"; "{line}");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info!("Starting Tauri app...");
 | 
					    info!("Starting Tauri app...");
 | 
				
			||||||
    let app = tauri::Builder::default()
 | 
					    let app = tauri::Builder::default()
 | 
				
			||||||
@ -373,82 +269,6 @@ async fn main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[get("/system/dotnet/port")]
 | 
					 | 
				
			||||||
fn dotnet_port(_token: APIToken) -> String {
 | 
					 | 
				
			||||||
    let dotnet_server_port = *DOTNET_SERVER_PORT;
 | 
					 | 
				
			||||||
    format!("{dotnet_server_port}")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[get("/system/dotnet/ready")]
 | 
					 | 
				
			||||||
async fn dotnet_ready(_token: APIToken) {
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // We create a manual scope for the lock to be released as soon as possible.
 | 
					 | 
				
			||||||
    // This is necessary because we cannot await any function while the lock is
 | 
					 | 
				
			||||||
    // held.
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let mut initialized = DOTNET_INITIALIZED.lock().unwrap();
 | 
					 | 
				
			||||||
        if *initialized {
 | 
					 | 
				
			||||||
            error!("Anyone tried to initialize the runtime twice. This is not intended.");
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        info!("The .NET server was booted successfully.");
 | 
					 | 
				
			||||||
        *initialized = true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Try to get the main window. If it is not available yet, wait for it:
 | 
					 | 
				
			||||||
    let mut main_window_ready = false;
 | 
					 | 
				
			||||||
    let mut main_window_status_reported = false;
 | 
					 | 
				
			||||||
    let main_window_spawn_clone = &MAIN_WINDOW;
 | 
					 | 
				
			||||||
    while !main_window_ready
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        main_window_ready = {
 | 
					 | 
				
			||||||
            let main_window = main_window_spawn_clone.lock().unwrap();
 | 
					 | 
				
			||||||
            main_window.is_some()
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if !main_window_ready {
 | 
					 | 
				
			||||||
            if !main_window_status_reported {
 | 
					 | 
				
			||||||
                info!("Waiting for main window to be ready, because .NET was faster than Tauri.");
 | 
					 | 
				
			||||||
                main_window_status_reported = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            time::sleep(Duration::from_millis(100)).await;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    let main_window = main_window_spawn_clone.lock().unwrap();
 | 
					 | 
				
			||||||
    let dotnet_server_port = *DOTNET_SERVER_PORT;
 | 
					 | 
				
			||||||
    let url = match Url::parse(format!("http://localhost:{dotnet_server_port}").as_str())
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Ok(url) => url,
 | 
					 | 
				
			||||||
        Err(msg) => {
 | 
					 | 
				
			||||||
            error!("Error while parsing URL for navigating to the app: {msg}");
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    let js_location_change = format!("window.location = '{url}';");
 | 
					 | 
				
			||||||
    let location_change_result = main_window.as_ref().unwrap().eval(js_location_change.as_str());
 | 
					 | 
				
			||||||
    match location_change_result {
 | 
					 | 
				
			||||||
        Ok(_) => info!("The app location was changed to {url}."),
 | 
					 | 
				
			||||||
        Err(e) => error!("Failed to change the app location to {url}: {e}."),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn stop_servers() {
 | 
					 | 
				
			||||||
    if let Some(server_process) = DOTNET_SERVER.lock().unwrap().take() {
 | 
					 | 
				
			||||||
        let server_kill_result = server_process.kill();
 | 
					 | 
				
			||||||
        match server_kill_result {
 | 
					 | 
				
			||||||
            Ok(_) => info!("The .NET server process was stopped."),
 | 
					 | 
				
			||||||
            Err(e) => error!("Failed to stop the .NET server process: {e}."),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        warn!("The .NET server process was not started or is already stopped.");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[get("/updates/check")]
 | 
					#[get("/updates/check")]
 | 
				
			||||||
async fn check_for_update(_token: APIToken) -> Json<CheckUpdateResponse> {
 | 
					async fn check_for_update(_token: APIToken) -> Json<CheckUpdateResponse> {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user