use crate::server::{AppError, AppState, AuthUser};
use anyhow::anyhow;
use axum::{
extract::{Path, State},
Json,
};
use serde::{Deserialize, Serialize};
use vault::fact::MemoFact;
#[derive(Deserialize)]
pub struct PushReq {
pub project_hash: String,
pub facts: Vec<MemoFact>,
pub client_version: String,
}
#[derive(Serialize)]
pub struct PushResp {
pub accepted: usize,
pub merged: usize,
pub server_version: u64,
}
pub async fn push_vault(
State(state): State<AppState>,
AuthUser(user_id): AuthUser,
Json(req): Json<PushReq>,
) -> Result<Json<PushResp>, AppError> {
let accepted = req.facts.len();
let facts_json = serde_json::to_string(&req.facts).map_err(|e| AppError(anyhow!(e)))?;
let version = state
.db
.upsert_vault(&user_id, &req.project_hash, &facts_json)
.map_err(|e| AppError(anyhow!(e)))?;
Ok(Json(PushResp {
accepted,
merged: 0,
server_version: version as u64,
}))
}
#[derive(Serialize)]
pub struct PullResp {
pub facts: Vec<MemoFact>,
pub server_version: u64,
}
pub async fn pull_vault(
State(state): State<AppState>,
AuthUser(user_id): AuthUser,
Path(project_hash): Path<String>,
) -> Result<Json<PullResp>, AppError> {
let record = state.db.get_vault(&user_id, &project_hash);
match record {
Some(r) => {
let facts: Vec<MemoFact> = serde_json::from_str(&r.facts_json).unwrap_or_default();
Ok(Json(PullResp {
facts,
server_version: r.server_version as u64,
}))
}
None => Ok(Json(PullResp {
facts: vec![],
server_version: 0,
})),
}
}
#[derive(Deserialize)]
pub struct BeaconPushReq {
pub tokens_sent: i64,
pub tokens_saved: i64,
pub skill: String,
}
pub async fn push_signals(
State(state): State<AppState>,
AuthUser(user_id): AuthUser,
Json(req): Json<BeaconPushReq>,
) -> Result<Json<serde_json::Value>, AppError> {
if !state.db.check_signal_rate_limit(&user_id) {
return Ok(Json(serde_json::json!({
"ok": false,
"reason": "rate_limited",
"limits": { "per_minute": 120, "per_day": 10000 }
})));
}
state
.db
.record_usage(&user_id, req.tokens_sent, req.tokens_saved, &req.skill)
.map_err(|e| AppError(anyhow!(e)))?;
Ok(Json(serde_json::json!({"ok": true})))
}