Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
504 changes: 354 additions & 150 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ thiserror = "1"
dotenvy = "0.15"
tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "trace"] }
futures = "0.3"

[profile.release]
opt-level = "z"
Expand Down
1 change: 1 addition & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
APP_ENV=development
DATABASE_URL=postgres://lance:lance@localhost:5432/lance
REDIS_URL=redis://localhost:6379
OPENCLAW_API_KEY=TODO_fill_in
OPENCLAW_BASE_URL=https://api.openclaw.ai/v1
STELLAR_RPC_URL=https://soroban-testnet.stellar.org
Expand Down
2 changes: 2 additions & 0 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ bytes = { workspace = true }
base64 = "0.22"
sha2 = "0.10"
ed25519-dalek = { version = "2", features = ["rand_core"] }
fred = { version = "9", features = ["enable-rustls", "i-all"] }
futures = { workspace = true }
hex = "0.4"
governor = { version = "0.6", features = ["std"] }
futures-util = "0.3"
Expand Down
19 changes: 18 additions & 1 deletion backend/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::services::cache::CacheService;
use crate::services::judge::JudgeService;
use crate::services::stellar::StellarService;
use sqlx::PgPool;
Expand All @@ -7,14 +8,30 @@ pub struct AppState {
pub pool: PgPool,
pub judge: std::sync::Arc<JudgeService>,
pub stellar: std::sync::Arc<StellarService>,
pub cache: Option<CacheService>,
}

impl AppState {
pub fn new(pool: PgPool) -> Self {
pub async fn new(pool: PgPool) -> Self {
let cache = match CacheService::from_env().await {
Ok(c) => {
tracing::info!("Redis cache initialized successfully");
Some(c)
}
Err(e) => {
tracing::warn!(
"Failed to initialize Redis cache: {}. Running without cache.",
e
);
None
}
};

Self {
pool,
judge: std::sync::Arc::new(JudgeService::from_env()),
stellar: std::sync::Arc::new(StellarService::from_env()),
cache,
}
}
}
3 changes: 1 addition & 2 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,9 @@ async fn main() -> anyhow::Result<()> {

sqlx::migrate!("./migrations").run(&pool).await?;

let state = AppState::new(pool.clone());
let state = AppState::new(pool.clone()).await;
tokio::spawn(worker::run_judge_worker(pool.clone()));
tokio::spawn(indexer::run_indexer_worker(pool));

let app = build_router(state);

let port: u16 = std::env::var("PORT")
Expand Down
35 changes: 32 additions & 3 deletions backend/src/routes/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,50 @@ pub async fn readiness(State(state): State<AppState>) -> (StatusCode, Json<Value
}

pub async fn health(State(state): State<AppState>) -> (StatusCode, Json<Value>) {
match sqlx::query("SELECT 1").execute(&state.pool).await {
let mut cache_status = "not configured".to_string();
let mut overall_status = "ok".to_string();

// Check database connection
let db_res = sqlx::query("SELECT 1").execute(&state.pool).await;

// Check Redis cache connection (if configured)
if let Some(ref cache) = state.cache {
match cache.ping().await {
Ok(pong) => {
cache_status = pong;
}
Err(e) => {
cache_status = format!("error: {}", e);
overall_status = "degraded".to_string();
}
}
}

match db_res {
Ok(_) => {
let (code, Json(sync_status_payload)) = sync_status(State(state.clone())).await;
let final_status = if overall_status == "degraded" {
"degraded"
} else {
sync_status_payload["status"].as_str().unwrap_or("ok")
};
(
code,
Json(json!({
"status": sync_status_payload["status"].clone(),
"status": final_status,
"db": "connected",
"cache": cache_status,
"indexer_sync_status": sync_status_payload
})),
)
}
Err(e) => (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({ "status": "degraded", "db": e.to_string() })),
Json(json!({
"status": "degraded",
"db": e.to_string(),
"cache": cache_status
})),
),
}
}
Expand Down
16 changes: 10 additions & 6 deletions backend/src/routes/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ async fn list_jobs(

if let Some(q) = params.query {
query_builder.push(" AND (title ILIKE ");
query_builder.push_bind(format!("%{}%", q));
query_builder.push_bind(format!("%{q}%"));
query_builder.push(" OR description ILIKE ");
query_builder.push_bind(format!("%{}%", q));
query_builder.push_bind(format!("%{q}%"));
query_builder.push(")");
}

Expand All @@ -71,21 +71,25 @@ async fn list_jobs(
if let Some(tag) = params.tag {
if tag != "all" {
query_builder.push(" AND (title ILIKE ");
query_builder.push_bind(format!("%{}%", tag));
query_builder.push_bind(format!("%{tag}%"));
query_builder.push(" OR description ILIKE ");
query_builder.push_bind(format!("%{}%", tag));
query_builder.push_bind(format!("%{tag}%"));
query_builder.push(")");
}
}

match params.sort.as_deref() {
Some("budget") => query_builder.push(" ORDER BY budget_usdc DESC"),
Some("budget") => {
query_builder.push(" ORDER BY budget_usdc DESC");
}
Some("reputation") => {
// Reputation sort requires joining with a reputation table or calculating score.
// For now, we'll just sort by created_at as a fallback.
query_builder.push(" ORDER BY created_at DESC");
}
_ => query_builder.push(" ORDER BY created_at DESC"),
_ => {
query_builder.push(" ORDER BY created_at DESC");
}
}

let jobs = query_builder
Expand Down
Loading