WIP: Support reading the server configuration from a git repo #31
This commit is contained in:
parent
ba67b1ebcb
commit
ffe952fee1
9 changed files with 148 additions and 13 deletions
|
@ -6,6 +6,7 @@ mod tests;
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
use git_next_server::{BranchName, Secret};
|
||||
use kxio::{fs, network::Network};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -23,7 +24,12 @@ enum Command {
|
|||
#[derive(Parser, Debug)]
|
||||
enum Server {
|
||||
Init,
|
||||
Start,
|
||||
Start {
|
||||
repo_url: Option<String>,
|
||||
api_key: Option<String>,
|
||||
branch_name: Option<String>,
|
||||
config_file: Option<PathBuf>,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -40,9 +46,25 @@ fn main() {
|
|||
Server::Init => {
|
||||
git_next_server::init(fs);
|
||||
}
|
||||
Server::Start => {
|
||||
Server::Start {
|
||||
repo_url,
|
||||
api_key,
|
||||
branch_name,
|
||||
config_file,
|
||||
} => {
|
||||
let sleep_duration = std::time::Duration::from_secs(10);
|
||||
git_next_server::start(fs, net, repository_factory, sleep_duration);
|
||||
git_next_server::start(
|
||||
fs,
|
||||
net,
|
||||
repository_factory,
|
||||
sleep_duration,
|
||||
(
|
||||
repo_url,
|
||||
api_key.map(Secret::new),
|
||||
branch_name.map(BranchName::new),
|
||||
config_file,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/// The API Token for the [user]
|
||||
/// ForgeJo: https://{hostname}/user/settings/applications
|
||||
/// Github: https://github.com/settings/tokens
|
||||
#[derive(Clone, Debug, derive_more::Constructor)]
|
||||
#[derive(Clone, Debug, derive_more::Constructor, derive_more::From)]
|
||||
pub struct ApiToken(secrecy::Secret<String>);
|
||||
/// The API Token is in effect a password, so it must be explicitly exposed to access its value
|
||||
impl secrecy::ExposeSecret<String> for ApiToken {
|
||||
|
|
|
@ -10,6 +10,7 @@ pub mod git_dir;
|
|||
mod host_name;
|
||||
mod newtype;
|
||||
mod registered_webhook;
|
||||
mod remote_repo_server_config;
|
||||
mod remote_url;
|
||||
mod repo_alias;
|
||||
mod repo_branches;
|
||||
|
@ -34,6 +35,7 @@ pub use git_dir::GitDir;
|
|||
pub use git_dir::StoragePathType;
|
||||
pub use host_name::Hostname;
|
||||
pub use registered_webhook::RegisteredWebhook;
|
||||
pub use remote_repo_server_config::RemoteRepoServerConfig;
|
||||
pub use remote_url::RemoteUrl;
|
||||
pub use repo_alias::RepoAlias;
|
||||
pub use repo_branches::RepoBranches;
|
||||
|
|
18
crates/config/src/remote_repo_server_config.rs
Normal file
18
crates/config/src/remote_repo_server_config.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use derive_more::Constructor;
|
||||
|
||||
use crate::{BranchName, RemoteUrl};
|
||||
|
||||
#[derive(Clone, Debug, Constructor, PartialEq, Eq)]
|
||||
pub struct RemoteRepoServerConfig {
|
||||
repo_url: RemoteUrl,
|
||||
api_key: String,
|
||||
branch_name: BranchName,
|
||||
config_file: PathBuf,
|
||||
}
|
||||
impl RemoteRepoServerConfig {
|
||||
pub fn api_key(&self) -> secrecy::Secret<String> {
|
||||
secrecy::Secret::new(self.api_key.clone())
|
||||
}
|
||||
}
|
|
@ -2,3 +2,4 @@ mod file_updated;
|
|||
mod receive_server_config;
|
||||
mod receive_valid_server_config;
|
||||
mod shutdown;
|
||||
mod watch_file;
|
||||
|
|
21
crates/server-actor/src/handlers/watch_file.rs
Normal file
21
crates/server-actor/src/handlers/watch_file.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
//-
|
||||
use actix::prelude::*;
|
||||
// use git_next_config::server::ServerConfig;
|
||||
|
||||
// use crate::messages::ReceiveServerConfig;
|
||||
use crate::{messages::WatchFile, Server};
|
||||
|
||||
impl Handler<WatchFile> for Server {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, _msg: WatchFile, _ctx: &mut Self::Context) -> Self::Result {
|
||||
// let server_config = match ServerConfig::load(&self.fs) {
|
||||
// Ok(server_config) => server_config,
|
||||
// Err(err) => {
|
||||
// tracing::error!("Failed to load config file. Error: {}", err);
|
||||
// return;
|
||||
// }
|
||||
// };
|
||||
// self.do_send(ReceiveServerConfig::new(server_config), ctx);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
//-
|
||||
use derive_more::Constructor;
|
||||
use git_next_actor_macros::message;
|
||||
use git_next_config::server::{ServerConfig, ServerStorage};
|
||||
use git_next_config::{
|
||||
server::{ServerConfig, ServerStorage},
|
||||
RemoteRepoServerConfig,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
// receive server config
|
||||
|
@ -21,3 +24,5 @@ pub struct ValidServerConfig {
|
|||
message!(ReceiveValidServerConfig: ValidServerConfig: "Notification of validated server configuration.");
|
||||
|
||||
message!(Shutdown: "Notification to shutdown the server actor");
|
||||
|
||||
message!(WatchFile: RemoteRepoServerConfig: "Start monitoring the repo for changes to the config file.");
|
||||
|
|
|
@ -23,6 +23,9 @@ kxio = { workspace = true }
|
|||
actix = { workspace = true }
|
||||
actix-rt = { workspace = true }
|
||||
|
||||
# Security
|
||||
secrecy = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Testing
|
||||
assert2 = { workspace = true }
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
//
|
||||
use actix::prelude::*;
|
||||
use git_next_config::{RemoteRepoServerConfig, RemoteUrl};
|
||||
use git_next_file_watcher_actor::{FileUpdated, FileWatcher};
|
||||
use git_next_server_actor::Server;
|
||||
use kxio::{fs::FileSystem, network::Network};
|
||||
use secrecy::ExposeSecret;
|
||||
use std::path::PathBuf;
|
||||
use tracing::{error, info, level_filters::LevelFilter};
|
||||
|
||||
pub use git_next_config::BranchName;
|
||||
pub use git_next_server_actor::{repository_factory, RepositoryFactory};
|
||||
pub use secrecy::Secret;
|
||||
|
||||
pub fn init(fs: FileSystem) {
|
||||
let file_name = "git-next-server.toml";
|
||||
|
@ -35,23 +39,55 @@ pub fn start(
|
|||
net: Network,
|
||||
repo: Box<dyn RepositoryFactory>,
|
||||
sleep_duration: std::time::Duration,
|
||||
(repo_url, api_key, branch_name, config_file): (
|
||||
Option<String>,
|
||||
Option<Secret<String>>,
|
||||
Option<BranchName>,
|
||||
Option<PathBuf>,
|
||||
),
|
||||
) {
|
||||
init_logging();
|
||||
|
||||
let Ok(remote_config) = parse_remote_repo_config(repo_url, api_key, branch_name, config_file)
|
||||
else {
|
||||
tracing::error!("Failed to parse repo config for server settings.");
|
||||
return;
|
||||
};
|
||||
|
||||
info!("Starting Server...");
|
||||
let execution = async move {
|
||||
let server = Server::new(fs.clone(), net.clone(), repo, sleep_duration).start();
|
||||
server.do_send(FileUpdated);
|
||||
|
||||
info!("Starting File Watcher...");
|
||||
let fw = match FileWatcher::new("git-next-server.toml".into(), server.clone().recipient()) {
|
||||
Ok(fw) => fw,
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to start file watcher");
|
||||
return;
|
||||
match remote_config {
|
||||
None => {
|
||||
info!("Starting File Watcher...");
|
||||
let fw = match FileWatcher::new(
|
||||
"git-next-server.toml".into(),
|
||||
server.clone().recipient(),
|
||||
) {
|
||||
Ok(fw) => fw,
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to start file watcher");
|
||||
return;
|
||||
}
|
||||
};
|
||||
fw.start();
|
||||
}
|
||||
};
|
||||
fw.start();
|
||||
Some(_remote_repo_server_config) => {
|
||||
info!("Starting Repo Watcher...");
|
||||
// TODO: start private repo actor and send watch file message
|
||||
// let rw =
|
||||
// match RepoWatcher::new(remote_repo_server_config, server.clone().recipient()) {
|
||||
// Ok(rw) => rw,
|
||||
// Err(err) => {
|
||||
// error!(?err, "Failed to start repo watcher");
|
||||
// return;
|
||||
// }
|
||||
// };
|
||||
// rw.start();
|
||||
}
|
||||
}
|
||||
|
||||
info!("Server running - Press Ctrl-C to stop...");
|
||||
let _ = actix_rt::signal::ctrl_c().await;
|
||||
|
@ -85,3 +121,30 @@ pub fn init_logging() {
|
|||
.with(subscriber)
|
||||
.init();
|
||||
}
|
||||
|
||||
fn parse_remote_repo_config(
|
||||
repo_url: Option<String>,
|
||||
api_key: Option<Secret<String>>,
|
||||
branch_name: Option<BranchName>,
|
||||
config_file: Option<PathBuf>,
|
||||
) -> Result<Option<RemoteRepoServerConfig>, ()> {
|
||||
match (repo_url, api_key, branch_name, config_file) {
|
||||
(Some(repo_url), Some(api_key), Some(branch_name), Some(config_file)) => {
|
||||
let Some(url) = RemoteUrl::parse(repo_url) else {
|
||||
tracing::error!("--repo-url is invalid");
|
||||
return Err(());
|
||||
};
|
||||
Ok(Some(RemoteRepoServerConfig::new(
|
||||
url,
|
||||
api_key.expose_secret().clone(),
|
||||
branch_name,
|
||||
config_file,
|
||||
)))
|
||||
}
|
||||
(None, None, None, None) => Ok(None),
|
||||
(_, _, _, _) => {
|
||||
tracing::error!("Server config requires all or none of 'repo-url', 'api-key', 'branch-name' and 'config-file', not all were provided");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue