meta: renamed config to state and wrapped in a server object
This commit is contained in:
@@ -4,7 +4,7 @@ use salvo::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::{common::AppResult, config::ConfigReader};
|
||||
use crate::{common::AppResult, state::StateReader};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AppListRespApp {
|
||||
@@ -37,7 +37,7 @@ struct GetAppsResponse {
|
||||
}
|
||||
|
||||
#[craft]
|
||||
impl crate::config::ConfigFile {
|
||||
impl crate::server::Server {
|
||||
#[craft(endpoint(status_codes(StatusCode::OK, StatusCode::INTERNAL_SERVER_ERROR)))]
|
||||
pub async fn get_apps(self: ::std::sync::Arc<Self>) -> AppResult<Json<GetAppsResponse>> {
|
||||
let standard_error = Err(crate::common::AppError {
|
||||
@@ -45,7 +45,7 @@ impl crate::config::ConfigFile {
|
||||
description: "failed to get available apps".to_string(),
|
||||
});
|
||||
|
||||
let reader = self.read().await;
|
||||
let reader = self.state.read().await;
|
||||
let unique_id = match reader.unique_id() {
|
||||
Ok(u) => u,
|
||||
Err(e) => {
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{self, File},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Server {
|
||||
pub name: String,
|
||||
pub host: String,
|
||||
pub base_port: u16,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn http_port(&self) -> u16 {
|
||||
self.base_port + 189
|
||||
}
|
||||
pub fn https_port(&self) -> u16 {
|
||||
self.base_port + 184
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Config {
|
||||
servers: HashMap<String, Server>,
|
||||
unique_id: String,
|
||||
}
|
||||
|
||||
pub struct ConfigFile {
|
||||
lock: tokio::sync::RwLock<()>,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
pub trait ConfigReader {
|
||||
fn servers(&self) -> Result<HashMap<String, Server>>;
|
||||
fn unique_id(&self) -> Result<String>;
|
||||
}
|
||||
|
||||
pub trait ConfigWriter {
|
||||
fn add_server(&self, server: Server) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct ConfigReadAccess<'a> {
|
||||
_guard: RwLockReadGuard<'a, ()>,
|
||||
config: &'a ConfigFile,
|
||||
}
|
||||
|
||||
pub struct ConfigWriteAccess<'a> {
|
||||
_guard: RwLockWriteGuard<'a, ()>,
|
||||
config: &'a ConfigFile,
|
||||
}
|
||||
|
||||
impl ConfigFile {
|
||||
fn load_config(&self) -> Result<Config> {
|
||||
tracing::debug!("parsing config file");
|
||||
|
||||
let config_file = File::open(&self.path)?;
|
||||
Ok(serde_json::from_reader(config_file)?)
|
||||
}
|
||||
|
||||
fn write_config(&self, config: Config) -> Result<()> {
|
||||
tracing::debug!("serializing config file");
|
||||
let config_file = File::create(&self.path)?;
|
||||
Ok(serde_json::to_writer_pretty(config_file, &config)?)
|
||||
}
|
||||
|
||||
pub fn new() -> Result<Self> {
|
||||
let project_dirs =
|
||||
directories::ProjectDirs::from("xyz", "ohea", "gamestream-webtransport-proxy")
|
||||
.ok_or(anyhow::anyhow!("Could not get project dirs"))?;
|
||||
|
||||
let data_dir = project_dirs.data_dir();
|
||||
fs::create_dir_all(data_dir)?;
|
||||
|
||||
let state_path = data_dir.join("state.json");
|
||||
|
||||
if let Err(e) = File::open(&state_path) {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
write_default_config_to_path(&state_path)?;
|
||||
} else {
|
||||
return Err(anyhow!(e));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ConfigFile {
|
||||
lock: tokio::sync::RwLock::new(()),
|
||||
path: state_path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigFile {
|
||||
pub async fn read(&self) -> ConfigReadAccess {
|
||||
ConfigReadAccess {
|
||||
_guard: self.lock.read().await,
|
||||
config: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn write(&self) -> ConfigWriteAccess {
|
||||
ConfigWriteAccess {
|
||||
_guard: self.lock.write().await,
|
||||
config: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ConfigReader for ConfigReadAccess<'a> {
|
||||
fn servers(&self) -> Result<HashMap<String, Server>> {
|
||||
let config = self.config.load_config()?;
|
||||
Ok(config.servers)
|
||||
}
|
||||
|
||||
fn unique_id(&self) -> Result<String> {
|
||||
let config = self.config.load_config()?;
|
||||
Ok(config.unique_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ConfigWriter for ConfigWriteAccess<'a> {
|
||||
fn add_server(&self, server: Server) -> Result<()> {
|
||||
let mut config = self.config.load_config()?;
|
||||
|
||||
if config.servers.contains_key(&server.name) {
|
||||
return Err(anyhow!(
|
||||
"cannot add duplicate server with name: {}",
|
||||
server.name
|
||||
));
|
||||
}
|
||||
|
||||
config.servers.insert(server.name.clone(), server);
|
||||
|
||||
self.config.write_config(config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unique_id() -> Result<String> {
|
||||
let mut bytes = [0u8; 8];
|
||||
openssl::rand::rand_bytes(&mut bytes)?;
|
||||
Ok(hex::encode(bytes))
|
||||
}
|
||||
|
||||
fn write_default_config_to_path(path: &PathBuf) -> Result<()> {
|
||||
let default_config = Config {
|
||||
servers: HashMap::new(),
|
||||
unique_id: get_unique_id()?,
|
||||
};
|
||||
|
||||
let config_file = File::create(path)?;
|
||||
|
||||
Ok(serde_json::to_writer_pretty(config_file, &default_config)?)
|
||||
}
|
||||
@@ -10,8 +10,9 @@ use moonlight_common_c_sys::{
|
||||
mod apps;
|
||||
mod certs;
|
||||
mod common;
|
||||
mod config;
|
||||
mod pair;
|
||||
mod server;
|
||||
mod state;
|
||||
|
||||
#[allow(unused)]
|
||||
fn get_server_info() -> _SERVER_INFORMATION {
|
||||
@@ -113,12 +114,12 @@ async fn main() -> anyhow::Result<()> {
|
||||
.with_max_level(tracing::Level::DEBUG)
|
||||
.init();
|
||||
|
||||
let config = config::ConfigFile::new()?;
|
||||
let config_arc = std::sync::Arc::new(config);
|
||||
let server = server::Server::new()?;
|
||||
let server_arc = std::sync::Arc::new(server);
|
||||
|
||||
let router = Router::new()
|
||||
.push(Router::with_path("pair").post(config_arc.post_pair()))
|
||||
.push(Router::with_path("apps").get(config_arc.get_apps()));
|
||||
.push(Router::with_path("pair").post(server_arc.post_pair()))
|
||||
.push(Router::with_path("apps").get(server_arc.get_apps()));
|
||||
let doc = OpenApi::new("test api", "0.0.1").merge_router(&router);
|
||||
let router = router
|
||||
.unshift(doc.into_router("/api-doc/openapi.json"))
|
||||
|
||||
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
use crate::common::{AppError, AppResult, get_url};
|
||||
use crate::config::{ConfigReader, ConfigWriter};
|
||||
use crate::state::{StateReader, StateWriter};
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
struct PostPairParams {
|
||||
@@ -291,7 +291,7 @@ async fn send_client_pairing_secret(
|
||||
}
|
||||
|
||||
#[craft]
|
||||
impl crate::config::ConfigFile {
|
||||
impl crate::server::Server {
|
||||
#[craft(endpoint(status_codes(
|
||||
StatusCode::OK,
|
||||
StatusCode::BAD_REQUEST,
|
||||
@@ -303,13 +303,13 @@ impl crate::config::ConfigFile {
|
||||
) -> AppResult<()> {
|
||||
let params = body.into_inner();
|
||||
|
||||
let server = crate::config::Server {
|
||||
let server = crate::state::GamestreamServer {
|
||||
host: params.host,
|
||||
base_port: params.base_port,
|
||||
name: params.name,
|
||||
};
|
||||
|
||||
let reader = self.read().await;
|
||||
let reader = self.state.read().await;
|
||||
|
||||
let servers = match reader.servers() {
|
||||
Ok(s) => s,
|
||||
@@ -333,7 +333,7 @@ impl crate::config::ConfigFile {
|
||||
});
|
||||
}
|
||||
|
||||
let unique_id = match self.read().await.unique_id() {
|
||||
let unique_id = match self.state.read().await.unique_id() {
|
||||
Ok(u) => u,
|
||||
Err(e) => {
|
||||
error!("Could not get unique id: {e}");
|
||||
@@ -457,10 +457,10 @@ impl crate::config::ConfigFile {
|
||||
);
|
||||
}
|
||||
|
||||
match self.write().await.add_server(server) {
|
||||
match self.state.write().await.add_server(server) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
error!("Could not write to config file: {e}");
|
||||
error!("Could not write to state file: {e}");
|
||||
return Err(AppError {
|
||||
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
description: "Pairing failed".to_string(),
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::state::StateFile;
|
||||
|
||||
pub struct Server {
|
||||
pub state: StateFile,
|
||||
}
|
||||
|
||||
|
||||
impl Server {
|
||||
pub fn new() -> Result<Self> {
|
||||
Ok(Server {
|
||||
state: StateFile::new()?
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{self, File},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GamestreamServer {
|
||||
pub name: String,
|
||||
pub host: String,
|
||||
pub base_port: u16,
|
||||
}
|
||||
|
||||
impl GamestreamServer {
|
||||
pub fn http_port(&self) -> u16 {
|
||||
self.base_port + 189
|
||||
}
|
||||
pub fn https_port(&self) -> u16 {
|
||||
self.base_port + 184
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct State {
|
||||
servers: HashMap<String, GamestreamServer>,
|
||||
unique_id: String,
|
||||
}
|
||||
|
||||
pub struct StateFile {
|
||||
lock: tokio::sync::RwLock<()>,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
pub trait StateReader {
|
||||
fn servers(&self) -> Result<HashMap<String, GamestreamServer>>;
|
||||
fn unique_id(&self) -> Result<String>;
|
||||
}
|
||||
|
||||
pub trait StateWriter {
|
||||
fn add_server(&self, server: GamestreamServer) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct StateReadAccess<'a> {
|
||||
_guard: RwLockReadGuard<'a, ()>,
|
||||
state: &'a StateFile,
|
||||
}
|
||||
|
||||
pub struct StateWriteAccess<'a> {
|
||||
_guard: RwLockWriteGuard<'a, ()>,
|
||||
state: &'a StateFile,
|
||||
}
|
||||
|
||||
impl StateFile {
|
||||
fn load_state(&self) -> Result<State> {
|
||||
tracing::debug!("parsing state file");
|
||||
|
||||
let state_file = File::open(&self.path)?;
|
||||
Ok(serde_json::from_reader(state_file)?)
|
||||
}
|
||||
|
||||
fn write_state(&self, state: State) -> Result<()> {
|
||||
tracing::debug!("serializing state file");
|
||||
let state_file = File::create(&self.path)?;
|
||||
Ok(serde_json::to_writer_pretty(state_file, &state)?)
|
||||
}
|
||||
|
||||
pub fn new() -> Result<Self> {
|
||||
let project_dirs =
|
||||
directories::ProjectDirs::from("xyz", "ohea", "gamestream-webtransport-proxy")
|
||||
.ok_or(anyhow::anyhow!("Could not get project dirs"))?;
|
||||
|
||||
let data_dir = project_dirs.data_dir();
|
||||
fs::create_dir_all(data_dir)?;
|
||||
|
||||
let state_path = data_dir.join("state.json");
|
||||
|
||||
if let Err(e) = File::open(&state_path) {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
write_default_state_to_path(&state_path)?;
|
||||
} else {
|
||||
return Err(anyhow!(e));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(StateFile {
|
||||
lock: tokio::sync::RwLock::new(()),
|
||||
path: state_path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StateFile {
|
||||
pub async fn read(&self) -> StateReadAccess {
|
||||
StateReadAccess {
|
||||
_guard: self.lock.read().await,
|
||||
state: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn write(&self) -> StateWriteAccess {
|
||||
StateWriteAccess {
|
||||
_guard: self.lock.write().await,
|
||||
state: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StateReader for StateReadAccess<'a> {
|
||||
fn servers(&self) -> Result<HashMap<String, GamestreamServer>> {
|
||||
let state = self.state.load_state()?;
|
||||
Ok(state.servers)
|
||||
}
|
||||
|
||||
fn unique_id(&self) -> Result<String> {
|
||||
let state = self.state.load_state()?;
|
||||
Ok(state.unique_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StateWriter for StateWriteAccess<'a> {
|
||||
fn add_server(&self, server: GamestreamServer) -> Result<()> {
|
||||
let mut state = self.state.load_state()?;
|
||||
|
||||
if state.servers.contains_key(&server.name) {
|
||||
return Err(anyhow!(
|
||||
"cannot add duplicate server with name: {}",
|
||||
server.name
|
||||
));
|
||||
}
|
||||
|
||||
state.servers.insert(server.name.clone(), server);
|
||||
|
||||
self.state.write_state(state)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_unique_id() -> Result<String> {
|
||||
let mut bytes = [0u8; 8];
|
||||
openssl::rand::rand_bytes(&mut bytes)?;
|
||||
Ok(hex::encode(bytes))
|
||||
}
|
||||
|
||||
fn write_default_state_to_path(path: &PathBuf) -> Result<()> {
|
||||
let default_state = State {
|
||||
servers: HashMap::new(),
|
||||
unique_id: get_unique_id()?,
|
||||
};
|
||||
|
||||
let state_file = File::create(path)?;
|
||||
|
||||
Ok(serde_json::to_writer_pretty(state_file, &default_state)?)
|
||||
}
|
||||
Reference in New Issue
Block a user