Skip to content

Commit

Permalink
Merge pull request #236 from Xeckt/config-rewrite
Browse files Browse the repository at this point in the history
PR #232 fixes
  • Loading branch information
zleyyij authored Oct 19, 2024
2 parents 8cd6e90 + ef3ba24 commit f530848
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 60 deletions.
32 changes: 20 additions & 12 deletions backend/src/app_conf.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::{fs, process};
use std::fs;
use std::process::exit;
use std::sync::Arc;
use serde::Deserialize;
use tracing::{info, error};
use tracing::{info, error, trace};

#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
pub struct AppConf {
Expand All @@ -15,6 +16,7 @@ pub struct AppConf {
pub struct Files {
pub asset_path: String,
pub docs_path: String,
pub repo_path: String,
pub repo_url: String,
}

Expand Down Expand Up @@ -55,6 +57,7 @@ trait ValidateFields {
}

// Macro to validate all fields for each struct
// TODO: Make it recognise if the type of the value supplied is also incorrect
macro_rules! impl_validate {
($struct_name:ident, $( $field:ident ),* ) => {
impl ValidateFields for $struct_name {
Expand All @@ -71,7 +74,7 @@ macro_rules! impl_validate {
};
}

impl_validate!(Files, asset_path, docs_path, repo_url);
impl_validate!(Files, asset_path, docs_path, repo_path, repo_url);
impl_validate!(Discord, admin_username);
impl_validate!(DiscordOAuth, client_id, secret, url, token_url);
impl_validate!(GitHubOAuth, client_id);
Expand All @@ -95,16 +98,21 @@ impl ValidateFields for AppConf {
}
}
impl AppConf {
pub fn load() -> Arc<Self> {
let file = fs::read_to_string("default.toml").expect("Unable to read config");
let config: Self = toml::from_str(&file).expect("Unable to parse config");
match config.validate("config") {
Ok(_) => info!("Configuration isn't empty"),
Err(e) => {
error!("Validation error: {}", e);
process::exit(1)
},

pub fn load(file: &str) -> Arc<Self> {
info!("Loading configuration file: {}", file);

if fs::metadata(file).is_err() {
error!("Configuration file {} does not exist", file);
exit(1)
}

let config: Self = toml::from_str(&fs::read_to_string(file).unwrap()).expect("Unable to parse config");

trace!("Loaded config: {:#?}", config);

config.validate("config").expect("Invalid config");

Arc::new(config)
}
}
28 changes: 13 additions & 15 deletions backend/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use std::{
sync::{Arc, Mutex},
};
use tracing::{debug, info, warn};
use crate::app_conf::AppConf;

#[derive(Clone)]
pub struct Interface {
Expand All @@ -37,10 +36,10 @@ impl Interface {
/// # Errors
/// This function will return an error if any of the git initialization steps fail, or if
/// the required environment variables are not set.
pub fn new(repo_url: String) -> Result<Self> {
let mut doc_path = PathBuf::from("repo");
doc_path.push(&repo_url);
let repo = Self::load_repository("repo", repo_url)?;
pub fn new(repo_url: String, repo_path: String, docs_path: String) -> Result<Self> {
let mut doc_path = PathBuf::from(&repo_path);
doc_path.push(docs_path);
let repo = Self::load_repository(&repo_url, &repo_path)?;
Ok(Self {
repo: Arc::new(Mutex::new(repo)),
doc_path,
Expand Down Expand Up @@ -123,13 +122,12 @@ impl Interface {
#[tracing::instrument(skip_all)]
pub fn put_doc<P: AsRef<Path> + Copy + std::fmt::Debug>(
&self,
config: Arc<AppConf>,
repo_url: &str,
path: P,
new_doc: &str,
message: &str,
token: &str,
) -> Result<()> {
let config = Arc::clone(&config);
let repo = self.repo.lock().unwrap();
let mut path_to_doc: PathBuf = PathBuf::from(".");
path_to_doc.push(&self.doc_path);
Expand All @@ -150,13 +148,13 @@ impl Interface {
})?;
let msg = format!("[Hyde]: {message}");
// Relative to the root of the repo, not the current dir, so typically `./docs` instead of `./repo/docs`
let mut relative_path = PathBuf::from(&config.files.repo_url);
let mut relative_path = PathBuf::from(&repo_url);
// Standard practice is to stage commits by adding them to an index.
relative_path.push(path);
Self::git_add(&repo, relative_path)?;
let commit_id = Self::git_commit(&repo, msg, None)?;
debug!("New commit made with ID: {:?}", commit_id);
Self::git_push(&repo, token, &config.files.repo_url)?;
Self::git_push(&repo, token, repo_url)?;
info!(
"Document {:?} edited and pushed to GitHub with message: {message:?}",
path.as_ref()
Expand Down Expand Up @@ -209,19 +207,19 @@ impl Interface {
/// If the repository at the provided path exists, open it and fetch the latest changes from the `master` branch.
/// If not, clone into the provided path.
#[tracing::instrument]
fn load_repository<P: AsRef<Path> + std::fmt::Debug>(path: P, repo_url: String) -> Result<Repository> {
if let Ok(repo) = Repository::open("./repo") {
fn load_repository(repo_url: &str, repo_path: &str) -> Result<Repository> {
if let Ok(repo) = Repository::open(repo_path) {
info!("Existing repository detected, fetching latest changes");
Self::git_pull(&repo)?;
info!("Existing repository detected, fetching latest changes...");
return Ok(repo);
}
let output_path = Path::new("./repo");

let output_path = Path::new(repo_path);
info!(
"No repo detected, cloning {repo_url:?} into {:?}...",
output_path.display()
);
let repo = Repository::clone(&repo_url, output_path)?;
let repo = Repository::clone(repo_url, output_path)?;
info!("Successfully cloned repo");
Ok(repo)
}
Expand Down
3 changes: 1 addition & 2 deletions backend/src/handlers_prelude/doc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::sync::Arc;
use axum::{debug_handler, extract::{Query, State}, http::{HeaderMap, StatusCode}, Json, Router};
use axum::routing::get;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -83,7 +82,7 @@ pub async fn put_doc_handler(

match state
.git
.put_doc(Arc::clone(&state.config), &body.path, &body.contents, &final_commit_message, &gh_token)
.put_doc(&state.config.files.repo_url, &body.path, &body.contents, &final_commit_message, &gh_token)
{
Ok(_) => Ok(StatusCode::CREATED),
Err(e) => {
Expand Down
50 changes: 19 additions & 31 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use reqwest::{
header::{ACCEPT, ALLOW, CONTENT_TYPE},
Client, Method,
};
use std::env::{self, current_exe};
use std::env::{current_exe};
use std::sync::Arc;
use std::time::Duration;
#[cfg(target_family = "unix")]
Expand All @@ -58,7 +58,7 @@ pub struct AppState {
db: Database,
}

#[derive(Parser)]
#[derive(Parser, Debug)]
struct Args {
#[arg(short, long, help = "The port the application listens on.", default_value_t = String::from("8080"))]
port: String,
Expand All @@ -72,25 +72,19 @@ struct Args {
)]
logging_level: Level,
#[arg(
short,
long,
help = "A list of config options as key value pairs supplied by passing this flag multiple times or providing a comma delimited list. \
This will set supplied config options as environment variables.",
value_parser = parse_key_val,
value_delimiter = ','
short = 'c',
long = "config",
help = "Pass your own .toml config file to Hyde.",
default_value_t = String::from("hyde-data/default.toml"),
)]
cfg: Vec<(String, String)>,
cfg: String,
}

#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
// Parse command line arguments
let cli_args = Args::parse();
// Load in any config settings passed by cli
for (key, value) in &cli_args.cfg {
env::set_var(key, value);
}
// Initialize logging
tracing_subscriber::fmt()
.with_max_level(cli_args.logging_level)
Expand All @@ -104,10 +98,10 @@ async fn main() -> Result<()> {
info!("Server running in release mode, version v{}", env!("CARGO_PKG_VERSION"));
}

// Initialize app state
let state: AppState = init_state()
.await
.wrap_err("Failed to initialize app state")?;
// Initialize app and config
let state: AppState = init_state(&cli_args).await.wrap_err("Failed to initialize app state")?;


debug!("Initialized app state");
// https://github.com/r-Techsupport/hyde/issues/27
// In docker, because the process is running with a PID of 1,
Expand All @@ -126,19 +120,23 @@ async fn main() -> Result<()> {
}
}


start_server(state, cli_args).await?;
Ok(())
}

/// Initialize an instance of [`AppState`]
#[tracing::instrument]
async fn init_state() -> Result<AppState> {
let config = AppConf::load();
async fn init_state(cli_args: &Args) -> Result<AppState> {
let config: Arc<AppConf> = AppConf::load(&cli_args.cfg);

let repo_url = config.files.repo_url.clone();
let git = task::spawn(async { git::Interface::new(repo_url)}).await??;
let repo_path = config.files.repo_path.clone();
let docs_path = config.files.docs_path.clone();

let git = task::spawn(async { git::Interface::new(repo_url, repo_path, docs_path)}).await??;
let reqwest_client = Client::new();

// We have to clone here, since the client will need to keep the values from the config.
let oauth = BasicClient::new(
ClientId::new(config.oauth.discord.client_id.clone()),
Some(ClientSecret::new(config.oauth.discord.secret.clone())),
Expand All @@ -157,16 +155,6 @@ async fn init_state() -> Result<AppState> {

}

/// Parse a single key-value pair for clap list parsing
///
/// https://github.com/clap-rs/clap_derive/blob/master/examples/keyvalue.rs
fn parse_key_val(s: &str) -> Result<(String, String), String> {
let pos = s
.find('=')
.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{}`", s))?;
Ok((s[..pos].to_string(), s[pos + 1..].to_string()))
}

async fn start_server(state: AppState, cli_args: Args) -> Result<()> {
// files are served relative to the location of the executable, not where the
// executable was run from
Expand Down
3 changes: 3 additions & 0 deletions default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
asset_path = "docs/"
# The location of the assets files relative to the root of the repo
docs_path = "assets/"
# The path where the repository will be pulled and used
repo_path = "repo/"
# The URL of the jekyll repository to interface with
repo_url = "https://github.com/r-Techsupport/rTS_Wiki.git"


# Discord is related to discord specific information to pass to Hyde.
[discord]
# The discord username of the admin account
Expand Down

0 comments on commit f530848

Please sign in to comment.