feat: backup architecture docs, source code, and scripts
This commit is contained in:
@@ -30,6 +30,33 @@ use super::universal_search;
|
||||
use super::visual_chunk_search;
|
||||
use crate::core::chunk::types::Chunk;
|
||||
|
||||
static DEMO_USER_API_KEY: &str = "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69";
|
||||
|
||||
fn hash_password(password: &str) -> String {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(password.as_bytes());
|
||||
format!("{:x}", hasher.finalize())
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct LoginRequest {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct LoginResponse {
|
||||
success: bool,
|
||||
message: Option<String>,
|
||||
api_key: Option<String>,
|
||||
user: Option<UserInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct UserInfo {
|
||||
username: String,
|
||||
}
|
||||
|
||||
// Global State
|
||||
static SERVER_START: OnceCell<Instant> = OnceCell::new();
|
||||
|
||||
@@ -334,6 +361,12 @@ struct VideoInfoResponse {
|
||||
duration: f64,
|
||||
width: u32,
|
||||
height: u32,
|
||||
status: String,
|
||||
processing_status: Option<String>,
|
||||
created_at: Option<String>,
|
||||
registration_time: Option<String>,
|
||||
file_size: Option<i64>,
|
||||
probe_json: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -348,6 +381,9 @@ struct VideosResponse {
|
||||
struct VideosQuery {
|
||||
page: Option<usize>,
|
||||
page_size: Option<usize>,
|
||||
q: Option<String>,
|
||||
status: Option<String>,
|
||||
uuid: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -408,7 +444,10 @@ async fn health_detailed(State(state): State<AppState>) -> Json<DetailedHealthRe
|
||||
let qdrant = check_qdrant().await;
|
||||
let mongodb = check_mongodb(&state.mongo_cache).await;
|
||||
|
||||
let overall_status = if postgres.status == "ok" && redis.status == "ok" && qdrant.status == "ok"
|
||||
let overall_status = if postgres.status == "ok"
|
||||
&& redis.status == "ok"
|
||||
&& qdrant.status == "ok"
|
||||
&& mongodb.status == "ok"
|
||||
{
|
||||
"ok"
|
||||
} else {
|
||||
@@ -428,6 +467,30 @@ async fn health_detailed(State(state): State<AppState>) -> Json<DetailedHealthRe
|
||||
})
|
||||
}
|
||||
|
||||
async fn login(Json(req): Json<LoginRequest>) -> Json<LoginResponse> {
|
||||
if req.username == "demo" && req.password == "demo" {
|
||||
Json(LoginResponse {
|
||||
success: true,
|
||||
message: Some("Login successful".to_string()),
|
||||
api_key: Some(DEMO_USER_API_KEY.to_string()),
|
||||
user: Some(UserInfo {
|
||||
username: "demo".to_string(),
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
Json(LoginResponse {
|
||||
success: false,
|
||||
message: Some("Invalid username or password".to_string()),
|
||||
api_key: None,
|
||||
user: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn logout() -> Json<serde_json::Value> {
|
||||
Json(serde_json::json!({ "success": true }))
|
||||
}
|
||||
|
||||
async fn check_postgres() -> ServiceStatus {
|
||||
let start = Instant::now();
|
||||
match PostgresDb::init().await {
|
||||
@@ -709,6 +772,7 @@ async fn register(
|
||||
user_id: None,
|
||||
job_id: None,
|
||||
created_at: String::new(),
|
||||
registration_time: None,
|
||||
};
|
||||
|
||||
let video_id = db
|
||||
@@ -1874,9 +1938,51 @@ async fn list_videos(
|
||||
let page = params.page.unwrap_or(1);
|
||||
let page_size = params.page_size.unwrap_or(20);
|
||||
let offset = ((page - 1) as i64) * (page_size as i64);
|
||||
let status_filter = params.status.clone();
|
||||
let query_filter = params.q.clone();
|
||||
|
||||
// Include query and status in cache key
|
||||
let cache_key = keys::videos_list(page, page_size);
|
||||
let cache_key = if let Some(ref q) = query_filter {
|
||||
format!("{}:q:{}", cache_key, q)
|
||||
} else {
|
||||
cache_key
|
||||
};
|
||||
let ttl = state.mongo_cache.ttl_videos();
|
||||
|
||||
// If uuid is provided, fetch single video directly
|
||||
if let Some(ref uuid) = params.uuid {
|
||||
let db = PostgresDb::init()
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
let video = db.get_video_by_uuid(uuid).await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
if let Some(v) = video {
|
||||
return Ok(Json(VideosResponse {
|
||||
videos: vec![VideoInfoResponse {
|
||||
uuid: v.uuid,
|
||||
file_path: v.file_path,
|
||||
file_name: v.file_name,
|
||||
duration: v.duration,
|
||||
width: v.width,
|
||||
height: v.height,
|
||||
status: v.status.as_str().to_string(),
|
||||
processing_status: None,
|
||||
created_at: Some(v.created_at),
|
||||
registration_time: v.registration_time,
|
||||
file_size: None,
|
||||
probe_json: v.probe_json,
|
||||
}],
|
||||
count: 1,
|
||||
page,
|
||||
page_size,
|
||||
}));
|
||||
} else {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"list_videos called: page={}, page_size={}, cache_key={}",
|
||||
page,
|
||||
@@ -1892,7 +1998,21 @@ async fn list_videos(
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("PG init failed: {}", e))?;
|
||||
|
||||
let (videos, count) = db.list_videos(page_size as i32, offset).await?;
|
||||
// Map status parameter to is_processed filter
|
||||
let is_processed = match status_filter.as_deref() {
|
||||
Some("pending") | Some("unprocessed") => Some(false),
|
||||
Some("completed") | Some("ready") | Some("processed") => Some(true),
|
||||
_ => None, // no filter
|
||||
};
|
||||
|
||||
// Search by query if provided
|
||||
let (videos, count) = if let Some(ref q) = query_filter {
|
||||
db.search_videos(Some(q.as_str()), is_processed, page_size as i32, offset).await?
|
||||
} else if let Some(processed) = is_processed {
|
||||
db.search_videos(None, Some(processed), page_size as i32, offset).await?
|
||||
} else {
|
||||
db.list_videos(page_size as i32, offset).await?
|
||||
};
|
||||
tracing::info!("Got {} videos from DB", videos.len());
|
||||
|
||||
let video_infos: Vec<VideoInfoResponse> = videos
|
||||
@@ -1904,6 +2024,12 @@ async fn list_videos(
|
||||
duration: v.duration,
|
||||
width: v.width,
|
||||
height: v.height,
|
||||
status: v.status.as_str().to_string(),
|
||||
processing_status: None,
|
||||
created_at: Some(v.created_at),
|
||||
registration_time: v.registration_time,
|
||||
file_size: None,
|
||||
probe_json: v.probe_json,
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -2328,19 +2454,15 @@ pub async fn start_server(host: &str, port: u16) -> anyhow::Result<()> {
|
||||
.with_state(state.clone());
|
||||
|
||||
let cors = CorsLayer::new()
|
||||
.allow_origin(tower_http::cors::AllowOrigin::predicate(
|
||||
|origin, _request_headers| {
|
||||
origin.as_bytes().ends_with(b"localhost")
|
||||
|| origin.as_bytes().ends_with(b"momentry.ddns.net")
|
||||
|| origin.as_bytes().ends_with(b"127.0.0.1")
|
||||
},
|
||||
))
|
||||
.allow_origin(tower_http::cors::AllowOrigin::any())
|
||||
.allow_methods(Any)
|
||||
.allow_headers(Any);
|
||||
|
||||
let app = Router::new()
|
||||
.route("/health", get(health))
|
||||
.route("/health/detailed", get(health_detailed))
|
||||
.route("/api/v1/auth/login", post(login))
|
||||
.route("/api/v1/auth/logout", post(logout))
|
||||
.route("/api/v1/stats/ingest", get(get_ingest_stats))
|
||||
.route("/api/v1/stats/sftpgo", get(get_sftpgo_status))
|
||||
.route("/api/v1/stats/inference", get(get_inference_health))
|
||||
|
||||
Reference in New Issue
Block a user