Job status work
This commit is contained in:
parent
2874d3a885
commit
c138940250
@ -15,7 +15,7 @@ use uuid::Uuid;
|
||||
|
||||
use crate::feed_utils::fetch_feed;
|
||||
use crate::feeds::Feed;
|
||||
use crate::jobs::{JobStatus, SharedJobStore};
|
||||
use crate::jobs::{JobStatus, JobType, SharedJobStore};
|
||||
use crate::user::AuthenticatedUser;
|
||||
use crate::Db;
|
||||
|
||||
@ -130,7 +130,7 @@ pub async fn import_opml(
|
||||
// Create a background job
|
||||
let job_id = {
|
||||
let mut store = job_store.write().await;
|
||||
store.create_job("opml_import".to_string())
|
||||
store.create_job(JobType::OpmlImport, user.user_id)
|
||||
};
|
||||
|
||||
// Launch background job
|
||||
@ -252,3 +252,39 @@ fn extract_feeds(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct JobStatusResponse {
|
||||
status: String,
|
||||
completed: usize,
|
||||
total: usize,
|
||||
}
|
||||
|
||||
#[get("/jobs/<job_id>")]
|
||||
pub async fn get_job_status(
|
||||
job_id: Uuid,
|
||||
job_store: &State<SharedJobStore>,
|
||||
user: AuthenticatedUser,
|
||||
) -> Result<Json<JobStatusResponse>, Status> {
|
||||
let store = job_store.read().await;
|
||||
let status = store
|
||||
.get_job_status(job_id, user.user_id)
|
||||
.ok_or(Status::NotFound)?;
|
||||
|
||||
let response = match status {
|
||||
JobStatus::InProgress { completed, total } => JobStatusResponse {
|
||||
status: "in_progress".to_string(),
|
||||
completed,
|
||||
total,
|
||||
},
|
||||
JobStatus::Completed { success_count } => JobStatusResponse {
|
||||
status: "completed".to_string(),
|
||||
completed: success_count,
|
||||
total: success_count,
|
||||
},
|
||||
JobStatus::Failed(_) => return Err(Status::InternalServerError),
|
||||
};
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
31
src/jobs.rs
31
src/jobs.rs
@ -3,18 +3,30 @@ use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum JobType {
|
||||
OpmlImport,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum JobStatus {
|
||||
InProgress { completed: usize, total: usize },
|
||||
Completed { success_count: usize },
|
||||
InProgress {
|
||||
completed: usize,
|
||||
total: usize,
|
||||
},
|
||||
Completed {
|
||||
success_count: usize,
|
||||
},
|
||||
#[allow(dead_code)]
|
||||
Failed(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Job {
|
||||
pub id: Uuid,
|
||||
pub job_type: String,
|
||||
pub job_type: JobType,
|
||||
pub status: JobStatus,
|
||||
pub user_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -29,7 +41,7 @@ impl JobStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_job(&mut self, job_type: String) -> Uuid {
|
||||
pub fn create_job(&mut self, job_type: JobType, user_id: Uuid) -> Uuid {
|
||||
let job_id = Uuid::new_v4();
|
||||
self.jobs.insert(
|
||||
job_id,
|
||||
@ -40,6 +52,7 @@ impl JobStore {
|
||||
completed: 0,
|
||||
total: 0,
|
||||
},
|
||||
user_id,
|
||||
},
|
||||
);
|
||||
job_id
|
||||
@ -51,8 +64,14 @@ impl JobStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_job_status(&self, job_id: Uuid) -> Option<JobStatus> {
|
||||
self.jobs.get(&job_id).map(|job| job.status.clone())
|
||||
pub fn get_job_status(&self, job_id: Uuid, user_id: Uuid) -> Option<JobStatus> {
|
||||
self.jobs.get(&job_id).and_then(|job| {
|
||||
if job.user_id == user_id {
|
||||
Some(job.status.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,6 +177,7 @@ fn rocket() -> _ {
|
||||
poll::poll_feed,
|
||||
poll::update_entry_state,
|
||||
import::import_opml,
|
||||
import::get_job_status,
|
||||
],
|
||||
)
|
||||
.mount("/static", FileServer::from("static"))
|
||||
|
@ -88,9 +88,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
showStatusModal(result.message, false);
|
||||
// TODO: Poll job status endpoint when implemented
|
||||
// For now, just refresh the feed list after a delay
|
||||
setTimeout(handleFeeds, 5000);
|
||||
if (result.job_id) {
|
||||
pollJobStatus(result.job_id);
|
||||
}
|
||||
} else {
|
||||
showStatusModal('OPML import failed: ' + result.message, true);
|
||||
}
|
||||
@ -106,6 +106,40 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
e.target.value = '';
|
||||
});
|
||||
|
||||
async function pollJobStatus(jobId) {
|
||||
const maxAttempts = 30; // 30 attempts * 2 second delay = 1 minute maximum
|
||||
let attempts = 0;
|
||||
|
||||
const poll = async () => {
|
||||
try {
|
||||
const response = await fetch(`/jobs/${jobId}`);
|
||||
if (response.ok) {
|
||||
const status = await response.json();
|
||||
if (status.status === 'completed') {
|
||||
showStatusModal(`Import completed. Successfully imported ${status.completed} feeds.`, false);
|
||||
handleFeeds();
|
||||
return;
|
||||
} else if (status.status === 'in_progress') {
|
||||
showStatusModal(`Importing feeds... ${status.completed}/${status.total} completed`, false);
|
||||
if (attempts++ < maxAttempts) {
|
||||
setTimeout(poll, 2000); // Poll every 2 seconds
|
||||
} else {
|
||||
showStatusModal('Import taking longer than expected. Check feeds list in a few minutes.', false);
|
||||
setTimeout(handleFeeds, 5000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error('Failed to fetch job status');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to poll job status:', error);
|
||||
showStatusModal('Failed to check import status. Please refresh the page.', true);
|
||||
}
|
||||
};
|
||||
|
||||
poll();
|
||||
}
|
||||
|
||||
// User menu handlers
|
||||
userMenuButton.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
|
Loading…
Reference in New Issue
Block a user