Skip to content

Commit 06b009b

Browse files
authored
Merge pull request #13 from habibi-dev/develop
💰 refactor(core): Jobs & state cleanup + add Iranian coins API
2 parents a00e9b5 + 6f4e857 commit 06b009b

30 files changed

Lines changed: 1117 additions & 183 deletions

Cargo.lock

Lines changed: 570 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rust_rest_api"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
edition = "2024"
55
license = "MIT"
66
authors = ["Habibi-Dev"]
@@ -22,8 +22,10 @@ sea-orm = { version = "1.1.16", features = ["sqlx-sqlite", "runtime-tokio-rustls
2222
once_cell = "1.21.3"
2323
rust-embed = "8.7.2"
2424
askama = { version = "0.14", features = ["full"] }
25-
http = "1.3.1"
2625
mime_guess = "2.0.5"
26+
reqwest = { version = "0.12.23", features = ["__rustls", "json", "rustls-tls"] }
27+
uuid = { version = "1.18.1", features = ["v4"] }
28+
anyhow = "1.0.100"
2729

2830
[dependencies.migration]
2931
path = "./migration"

src/app.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use crate::core::config::Config;
2+
use crate::jobs::{cache_flush::CacheFlushJob, coin_sync::CoinSyncJob};
3+
use crate::server;
4+
use crate::services::{cache::StatsCache, routes::Routes};
5+
use crate::state::{self, APP_STATE, AppState};
6+
use anyhow::{Context, Result};
7+
use migration::{Migrator, MigratorTrait};
8+
use sea_orm::Database;
9+
use std::sync::Arc;
10+
11+
pub async fn run() -> Result<()> {
12+
let config = load_config()?;
13+
let db = setup_database().await?;
14+
let cache = Arc::new(StatsCache::new(db.clone()));
15+
16+
state::State::init(db, Arc::clone(&cache));
17+
18+
start_background_jobs(&cache)?;
19+
20+
let app_state = get_app_state()?;
21+
let app = Routes::routes(app_state);
22+
23+
server::http::start_http(app, &config)
24+
.await
25+
.map_err(|e| anyhow::anyhow!("Failed to start HTTP server: {}", e))?;
26+
27+
Ok(())
28+
}
29+
30+
fn load_config() -> Result<Config> {
31+
Ok(Config::from_env())
32+
}
33+
34+
async fn setup_database() -> Result<sea_orm::DatabaseConnection> {
35+
let db_url =
36+
std::env::var("DATABASE_URL").context("DATABASE_URL environment variable is required")?;
37+
38+
let db = Database::connect(&db_url)
39+
.await
40+
.context("Failed to connect to database")?;
41+
42+
Migrator::up(&db, None)
43+
.await
44+
.context("Database migration failed")?;
45+
46+
Ok(db)
47+
}
48+
49+
fn start_background_jobs(cache: &Arc<StatsCache>) -> Result<()> {
50+
let flush_job = CacheFlushJob::new(Arc::clone(cache));
51+
let coin_job = CoinSyncJob::new();
52+
53+
crate::services::jobs::FlushJob::start(vec![flush_job.into_task(), coin_job.into_task()], None);
54+
55+
Ok(())
56+
}
57+
58+
fn get_app_state() -> Result<AppState> {
59+
APP_STATE
60+
.get()
61+
.cloned()
62+
.context("Application state not initialized")
63+
}

src/assets/index-Bu-dXgft.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/assets/index-C2f6evCj.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 101 additions & 69 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/jobs/cache_flush.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use crate::jobs::JobTask;
2+
use crate::services::cache::StatsCache;
3+
use std::sync::Arc;
4+
5+
pub struct CacheFlushJob {
6+
cache: Arc<StatsCache>,
7+
}
8+
9+
impl CacheFlushJob {
10+
pub fn new(cache: Arc<StatsCache>) -> Self {
11+
Self { cache }
12+
}
13+
14+
pub fn into_task(self) -> JobTask {
15+
let cache = self.cache;
16+
17+
Arc::new(move || {
18+
let cache_clone = Arc::clone(&cache);
19+
Box::pin(async move {
20+
if let Err(e) = cache_clone.flush_to_db().await {
21+
eprintln!("Cache flush job failed: {}", e);
22+
}
23+
})
24+
})
25+
}
26+
}

src/jobs/coin_sync.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use crate::jobs::JobTask;
2+
use crate::services::coin_ir::request::RequestCoinIr;
3+
use crate::state::APP_STATE;
4+
use std::sync::Arc;
5+
6+
pub struct CoinSyncJob;
7+
8+
impl CoinSyncJob {
9+
pub fn new() -> Self {
10+
Self
11+
}
12+
13+
pub fn into_task(self) -> JobTask {
14+
Arc::new(|| {
15+
Box::pin(async {
16+
if let Err(e) = sync_coins().await {
17+
eprintln!("Coin sync job failed: {}", e);
18+
}
19+
})
20+
})
21+
}
22+
}
23+
24+
async fn sync_coins() -> Result<(), Box<dyn std::error::Error>> {
25+
let service = RequestCoinIr::new();
26+
let coins = service.coins_typed().await?;
27+
28+
let state = APP_STATE.get().ok_or("Application state not available")?;
29+
30+
state
31+
.coin_tx
32+
.send(coins)
33+
.map_err(|_| "Failed to send coins to channel")?;
34+
35+
Ok(())
36+
}

src/jobs/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub mod cache_flush;
2+
pub mod coin_sync;
3+
4+
use std::{future::Future, pin::Pin, sync::Arc};
5+
6+
pub type JobTask = Arc<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;

src/main.rs

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,18 @@
1+
mod app;
12
mod core;
23
mod entities;
4+
mod jobs;
35
mod middleware;
46
mod repository;
57
mod server;
68
mod services;
9+
mod state;
710
mod utility;
811

9-
use crate::services::{cache::StatsCache, jobs::FlushJob, routes::Routes};
10-
use core::config::Config;
11-
use migration::{Migrator, MigratorTrait};
12-
use sea_orm::Database;
13-
use std::{sync::OnceLock, time::Instant};
14-
15-
static START: OnceLock<Instant> = OnceLock::new();
16-
1712
#[tokio::main]
1813
async fn main() {
19-
START.set(Instant::now()).ok();
20-
21-
let config = Config::from_env();
22-
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL is required");
23-
24-
let db = Database::connect(&db_url).await.expect("DB connect failed");
25-
Migrator::up(&db, None).await.expect("Migration failed");
26-
27-
let cache = StatsCache::new(db.clone());
28-
29-
// Start background flush job
30-
FlushJob::start(cache.clone());
31-
32-
let state = services::AppState {
33-
_db: db,
34-
stats_cache: cache,
35-
};
36-
let app = Routes::routes(state);
37-
38-
if let Err(e) = server::http::start_http(app, &config).await {
39-
eprintln!("Fatal server error: {}", e);
14+
if let Err(e) = app::run().await {
15+
eprintln!("Application failed to start: {}", e);
4016
std::process::exit(1);
4117
}
4218
}

0 commit comments

Comments
 (0)