Skip to content

Refactor runtime #192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Refactored the runtime API handling
  • Loading branch information
SommerEngineering committed Nov 5, 2024
commit e6789dcfeb6aeb819dbae2b8df24422410293a5c
6 changes: 3 additions & 3 deletions runtime/src/dotnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::certificate::CERTIFICATE_FINGERPRINT;
use crate::encryption::ENCRYPTION;
use crate::environment::is_dev;
use crate::network::get_available_port;
use crate::runtime_api::API_SERVER_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
Expand All @@ -31,7 +32,7 @@ pub fn dotnet_port(_token: APIToken) -> String {
format!("{dotnet_server_port}")
}

pub fn start_dotnet_server(api_server_port: u16) {
pub fn start_dotnet_server() {

// Get the secret password & salt and convert it to a base64 string:
let secret_password = BASE64_STANDARD.encode(ENCRYPTION.secret_password);
Expand All @@ -47,8 +48,7 @@ pub fn start_dotnet_server(api_server_port: u16) {
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 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
Expand Down
1 change: 1 addition & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ pub mod api_token;
pub mod app_window;
pub mod secret;
pub mod clipboard;
pub mod runtime_api;
pub mod certificate;
98 changes: 5 additions & 93 deletions runtime/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,13 @@
extern crate rocket;
extern crate core;

use std::collections::HashSet;
use once_cell::sync::Lazy;

use log::{info, warn};
use rocket::figment::Figment;
use rocket::routes;
use rocket::config::{Shutdown};
use mindwork_ai_studio::app_window::start_tauri;
use mindwork_ai_studio::certificate::{generate_certificate, CERTIFICATE, CERTIFICATE_PRIVATE_KEY};
use mindwork_ai_studio::certificate::{generate_certificate};
use mindwork_ai_studio::dotnet::start_dotnet_server;
use mindwork_ai_studio::environment::is_dev;
use mindwork_ai_studio::log::init_logging;
use mindwork_ai_studio::network::get_available_port;

// 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
// is necessary because we cannot communicate the port to the .NET server in the development
// environment.
static API_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
if is_dev() {
5000
} else {
get_available_port().unwrap()
}
});
use mindwork_ai_studio::runtime_api::start_runtime_api;

#[tokio::main]
async fn main() {
Expand All @@ -48,6 +30,7 @@ async fn main() {
init_logging();

info!("Starting MindWork AI Studio:");

let working_directory = std::env::current_dir().unwrap();
info!(".. The working directory is: '{working_directory:?}'");

Expand All @@ -66,78 +49,7 @@ async fn main() {
}

generate_certificate();

let api_port = *API_SERVER_PORT;
info!("Try to start the API server on 'http://localhost:{api_port}'...");

// The shutdown configuration for the runtime API server:
let mut shutdown = Shutdown {
// We do not want to use the Ctrl+C signal to stop the server:
ctrlc: false,

// Everything else is set to default for now:
..Shutdown::default()
};

#[cfg(unix)]
{
// We do not want to use the termination signal to stop the server.
// This option, however, is only available on Unix systems:
shutdown.signals = HashSet::new();
}

// Configure the runtime API server:
let figment = Figment::from(rocket::Config::release_default())

// We use the next available port which was determined before:
.merge(("port", api_port))

// The runtime API server should be accessible only from the local machine:
.merge(("address", "127.0.0.1"))

// We do not want to use the Ctrl+C signal to stop the server:
.merge(("ctrlc", false))

// Set a name for the server:
.merge(("ident", "AI Studio Runtime API"))

// Set the maximum number of workers and blocking threads:
.merge(("workers", 3))
.merge(("max_blocking", 12))

// No colors and emojis in the log output:
.merge(("cli_colors", false))

// Read the TLS certificate and key from the generated certificate data in-memory:
.merge(("tls.certs", CERTIFICATE.get().unwrap()))
.merge(("tls.key", CERTIFICATE_PRIVATE_KEY.get().unwrap()))

// Set the shutdown configuration:
.merge(("shutdown", shutdown));

//
// Start the runtime API server in a separate thread. This is necessary
// because the server is blocking, and we need to run the Tauri app in
// parallel:
//
tauri::async_runtime::spawn(async move {
rocket::custom(figment)
.mount("/", routes![
mindwork_ai_studio::dotnet::dotnet_port,
mindwork_ai_studio::dotnet::dotnet_ready,
mindwork_ai_studio::clipboard::set_clipboard,
mindwork_ai_studio::app_window::check_for_update,
mindwork_ai_studio::app_window::install_update,
mindwork_ai_studio::secret::get_secret,
mindwork_ai_studio::secret::store_secret,
mindwork_ai_studio::secret::delete_secret,
mindwork_ai_studio::environment::get_data_directory,
mindwork_ai_studio::environment::get_config_directory,
])
.ignite().await.unwrap()
.launch().await.unwrap();
});

start_dotnet_server(*API_SERVER_PORT);
start_runtime_api();
start_dotnet_server();
start_tauri();
}
94 changes: 94 additions & 0 deletions runtime/src/runtime_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use std::collections::HashSet;
use log::info;
use once_cell::sync::Lazy;
use rocket::config::Shutdown;
use rocket::figment::Figment;
use rocket::routes;
use crate::certificate::{CERTIFICATE, CERTIFICATE_PRIVATE_KEY};
use crate::environment::is_dev;
use crate::network::get_available_port;

// 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
// is necessary because we cannot communicate the port to the .NET server in the development
// environment.
pub static API_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
if is_dev() {
5000
} else {
get_available_port().unwrap()
}
});

pub fn start_runtime_api() {
let api_port = *API_SERVER_PORT;
info!("Try to start the API server on 'http://localhost:{api_port}'...");

// The shutdown configuration for the runtime API server:
let mut shutdown = Shutdown {
// We do not want to use the Ctrl+C signal to stop the server:
ctrlc: false,

// Everything else is set to default for now:
..Shutdown::default()
};

#[cfg(unix)]
{
// We do not want to use the termination signal to stop the server.
// This option, however, is only available on Unix systems:
shutdown.signals = HashSet::new();
}

// Configure the runtime API server:
let figment = Figment::from(rocket::Config::release_default())

// We use the next available port which was determined before:
.merge(("port", api_port))

// The runtime API server should be accessible only from the local machine:
.merge(("address", "127.0.0.1"))

// We do not want to use the Ctrl+C signal to stop the server:
.merge(("ctrlc", false))

// Set a name for the server:
.merge(("ident", "AI Studio Runtime API"))

// Set the maximum number of workers and blocking threads:
.merge(("workers", 3))
.merge(("max_blocking", 12))

// No colors and emojis in the log output:
.merge(("cli_colors", false))

// Read the TLS certificate and key from the generated certificate data in-memory:
.merge(("tls.certs", CERTIFICATE.get().unwrap()))
.merge(("tls.key", CERTIFICATE_PRIVATE_KEY.get().unwrap()))

// Set the shutdown configuration:
.merge(("shutdown", shutdown));

//
// Start the runtime API server in a separate thread. This is necessary
// because the server is blocking, and we need to run the Tauri app in
// parallel:
//
tauri::async_runtime::spawn(async move {
rocket::custom(figment)
.mount("/", routes![
crate::dotnet::dotnet_port,
crate::dotnet::dotnet_ready,
crate::clipboard::set_clipboard,
crate::app_window::check_for_update,
crate::app_window::install_update,
crate::secret::get_secret,
crate::secret::store_secret,
crate::secret::delete_secret,
crate::environment::get_data_directory,
crate::environment::get_config_directory,
])
.ignite().await.unwrap()
.launch().await.unwrap();
});
}