feat: add price handler, commit, push, pull
- price handler for getting or editing price (only applied to main profile) - routine pull sync recipe repo & backup commit recover Signed-off-by: Pakin <pakin.t@forth.co.th>
This commit is contained in:
parent
ab84060ab5
commit
0f857445a4
Notes:
pakin
2026-05-05 20:35:20 +07:00
feat: commit - not support multiple files yet feat: routine backup commit flush - not support order of commit yet, this may results in random commit
9 changed files with 610 additions and 23 deletions
167
src/app.rs
167
src/app.rs
|
|
@ -4,14 +4,21 @@ use axum::{
|
|||
routing::{get, post},
|
||||
serve::ListenerExt,
|
||||
};
|
||||
use log::{error, info};
|
||||
use log::{error, info, warn};
|
||||
use redis::TypedCommands;
|
||||
use reqwest::{StatusCode, multipart};
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
env,
|
||||
fs::{self, File},
|
||||
io::BufReader,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::{
|
||||
fs::read_dir,
|
||||
sync::{Mutex, mpsc::Sender},
|
||||
};
|
||||
use tokio::sync::{Mutex, mpsc::Sender};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Hub {
|
||||
|
|
@ -52,6 +59,18 @@ impl DevConfig {
|
|||
format!("{}/checkout?path={}", self.get_recipe_url(), path)
|
||||
}
|
||||
|
||||
pub fn get_post_file_to_recipe_repo(&self) -> String {
|
||||
format!("{}/commit", self.get_recipe_url())
|
||||
}
|
||||
|
||||
pub fn get_pull_recipe_repo(&self) -> String {
|
||||
format!("{}/pull", self.get_recipe_url())
|
||||
}
|
||||
|
||||
pub fn get_push_recipe_repo(&self) -> String {
|
||||
format!("{}/push", self.get_recipe_url())
|
||||
}
|
||||
|
||||
pub fn get_api_header(&self) -> (String, String) {
|
||||
("X-API-Key".to_string(), self.api_key.clone())
|
||||
}
|
||||
|
|
@ -83,7 +102,7 @@ impl AppState {
|
|||
let redis_cli_clone = redis_cli.clone();
|
||||
let tx_new = system_tx.clone();
|
||||
let result = Arc::new(AppState {
|
||||
dev_config,
|
||||
dev_config: dev_config.clone(),
|
||||
redis_cli,
|
||||
system_tx,
|
||||
connectors_mapping: Arc::new(Mutex::new(Hub {
|
||||
|
|
@ -91,6 +110,72 @@ impl AppState {
|
|||
})),
|
||||
});
|
||||
|
||||
// backup job
|
||||
let dev_config_backup = dev_config.clone();
|
||||
tokio::spawn(async move {
|
||||
let m_cfg = dev_config_backup.clone();
|
||||
|
||||
loop {
|
||||
// auto sync
|
||||
if invoke_pull_sync_request(m_cfg.clone()).await.is_err() {
|
||||
warn!("pulling repo unhealthy, retry again in 5 minutes");
|
||||
continue;
|
||||
}
|
||||
|
||||
match read_dir(".").await {
|
||||
Ok(mut d) => {
|
||||
while let Ok(Some(entry)) = d.next_entry().await {
|
||||
let ent_path = entry.path();
|
||||
|
||||
if let Some(filename) = ent_path.file_name()
|
||||
&& let Some(filename_str) = filename.to_str()
|
||||
&& filename_str.starts_with("gtx")
|
||||
&& filename_str.ends_with(".json")
|
||||
{
|
||||
// read file
|
||||
//
|
||||
let f = match File::open(ent_path.clone()) {
|
||||
Ok(f) => f,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let buf = BufReader::new(f);
|
||||
|
||||
let commit_from_backup: CommitPayload =
|
||||
match serde_json::from_reader(buf) {
|
||||
Ok(cm) => cm,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
if invoke_pull_sync_request(m_cfg.clone()).await.is_err() {
|
||||
warn!("pulling repo unhealthy, retry again in 5 minutes");
|
||||
continue;
|
||||
}
|
||||
|
||||
let _ =
|
||||
invoke_commit_request(m_cfg.clone(), commit_from_backup).await;
|
||||
|
||||
if invoke_push_request(m_cfg.clone()).await.is_ok() {
|
||||
// push success
|
||||
info!("push backup success");
|
||||
if fs::remove_file(ent_path.clone()).is_ok() {
|
||||
info!("clean backup");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
info!("[backup] idle");
|
||||
|
||||
tokio::time::sleep(Duration::from_mins(5)).await;
|
||||
}
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut lredis = redis_cli_clone.clone();
|
||||
let current_queue: crossbeam_queue::ArrayQueue<CommandRequestPayload> =
|
||||
|
|
@ -212,6 +297,82 @@ pub async fn invoke_checkout_request(
|
|||
}
|
||||
}
|
||||
|
||||
/// Invoke git pull, may takes sometime
|
||||
pub async fn invoke_pull_sync_request(
|
||||
config: DevConfig,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let req_path = config.get_pull_recipe_repo();
|
||||
// println!("dbg: {req_path}");
|
||||
let res = client.get(req_path).send().await?;
|
||||
|
||||
if res.status() != StatusCode::OK {
|
||||
// pull fail
|
||||
|
||||
error!(
|
||||
"invoke pull fail: [{}] {:?}",
|
||||
res.status(),
|
||||
res.text().await
|
||||
);
|
||||
return Err("pull fail".into());
|
||||
}
|
||||
|
||||
match res.text().await {
|
||||
Ok(raw) => Ok(raw),
|
||||
Err(e) => Err(format!("{e}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke sending from server to server for committing
|
||||
pub async fn invoke_commit_request(
|
||||
config: DevConfig,
|
||||
payload: CommitPayload,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
let commit_path = config.get_post_file_to_recipe_repo();
|
||||
let form = multipart::Form::new()
|
||||
.text("message", payload.message)
|
||||
.text("signature_username", payload.signature_username)
|
||||
.text("signature_email", payload.signature_email)
|
||||
.text("path", payload.path)
|
||||
.part(
|
||||
"file",
|
||||
multipart::Part::bytes(payload.file_bytes)
|
||||
.mime_str("application/octet-stream")
|
||||
.unwrap(),
|
||||
);
|
||||
let response = client.post(commit_path).multipart(form).send().await?;
|
||||
|
||||
info!("commit status: {}", response.status());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn invoke_push_request(config: DevConfig) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let req_path = config.get_push_recipe_repo();
|
||||
// println!("dbg: {req_path}");
|
||||
let res = client.get(req_path).send().await?;
|
||||
|
||||
if res.status() != StatusCode::OK {
|
||||
// pull fail
|
||||
|
||||
error!(
|
||||
"invoke push fail: [{}] {:?}",
|
||||
res.status(),
|
||||
res.text().await
|
||||
);
|
||||
return Err("push fail".into());
|
||||
}
|
||||
|
||||
match res.text().await {
|
||||
Ok(raw) => Ok(raw),
|
||||
Err(e) => Err(format!("{e}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_recipe_repo_router() -> Router<Arc<AppState>> {
|
||||
Router::new().route("/ws", get(crate::websocket::handler::websocket_handler))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue