Skip to content

Commit

Permalink
Add jobs dash
Browse files Browse the repository at this point in the history
  • Loading branch information
kirkbyers committed Nov 1, 2024
1 parent 61c422e commit 6d4668c
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 3 deletions.
2 changes: 0 additions & 2 deletions src/jobs/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ pub async fn process_job() -> Result<(), std::io::Error> {
conn.execute(&update_query, ())
.await
.expect("Failed to update job status");
db.sync().await.expect("Failed to sync jobs db");

match pending_job_type {
i if i == jobs::JobType::SMScrape.as_i32() => {
Expand All @@ -85,6 +84,5 @@ pub async fn process_job() -> Result<(), std::io::Error> {
.await
.expect("Failed to update job status");

db.sync().await.expect("Failed to sync jobs db");
Ok(())
}
16 changes: 16 additions & 0 deletions src/routes/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,19 @@ async fn home(conn: web::Data<libsql::Connection>) -> HttpResponse {

HttpResponse::Ok().body(rendered)
}

#[get("/jobs")]
async fn jobs_page(_conn: web::Data<libsql::Connection>) -> HttpResponse {
let tera_context = Context::new();

let rendered = match Tera::one_off(include_str!("../templates/jobs.html"), &tera_context, true)
{
Ok(rendered) => rendered,
Err(e) => {
eprintln!("Failed to render template: {:?}", e);
return HttpResponse::InternalServerError().finish();
}
};

HttpResponse::Ok().body(rendered)
}
4 changes: 3 additions & 1 deletion src/startup.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{
db,
routes::{
get_jobs, get_scrapes, health_check_route, home, make_green_rec, start_job, subscribe,
get_jobs, get_scrapes, health_check_route, home, jobs_page, make_green_rec, start_job,
subscribe,
},
};
use actix_web::{dev::Server, web, App, HttpServer};
Expand Down Expand Up @@ -42,6 +43,7 @@ pub async fn run(listener: TcpListener) -> Result<Server, std::io::Error> {
.service(web::scope("/jobs").service(get_jobs).service(start_job)),
)
.service(home)
.service(jobs_page)
.app_data(connection_data.clone())
})
.listen(listener)?
Expand Down
238 changes: 238 additions & 0 deletions src/templates/jobs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jobs Dashboard</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
background: #f5f5f5;
}

.container {
max-width: 1200px;
margin: 0 auto;
}

.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}

.title {
font-size: 24px;
color: #333;
}

.job-controls {
display: flex;
gap: 10px;
}

select, button {
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}

button {
background: #0066cc;
color: white;
border: none;
cursor: pointer;
transition: background 0.2s;
}

button:hover {
background: #0052a3;
}

button:disabled {
background: #ccc;
cursor: not-allowed;
}

.jobs-table {
width: 100%;
border-collapse: collapse;
background: white;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border-radius: 4px;
overflow: hidden;
}

.jobs-table th,
.jobs-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #eee;
}

.jobs-table th {
background: #f8f9fa;
font-weight: 600;
color: #555;
}

.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}

.status-pending {
background: #fff3cd;
color: #856404;
}

.status-completed {
background: #d4edda;
color: #155724;
}

.error-message {
background: #f8d7da;
color: #721c24;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
display: none;
}

.refresh-button {
background: #6c757d;
}

.refresh-button:hover {
background: #5a6268;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1 class="title">Jobs Dashboard</h1>
<div class="job-controls">
<select id="jobType">
<option value="SMScrape">SM Scrape</option>
<option value="Embed">Embed Scrapes</option>
</select>
<button id="startJob">Start New Job</button>
<button id="refreshJobs" class="refresh-button">Refresh</button>
</div>
</div>

<div id="errorMessage" class="error-message"></div>

<table class="jobs-table">
<thead>
<tr>
<th>ID</th>
<th>Type</th>
<th>Status</th>
<th>Created At</th>
<th>Completed At</th>
</tr>
</thead>
<tbody id="jobsTableBody">
</tbody>
</table>
</div>

<script>
const API_BASE_URL = '/api/jobs';

async function fetchJobs() {
try {
const response = await fetch(API_BASE_URL);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const jobs = await response.json();
displayJobs(jobs);
hideError();
} catch (error) {
showError('Failed to fetch jobs: ' + error.message);
}
}

async function startJob() {
const jobType = document.getElementById('jobType').value;
try {
const response = await fetch(API_BASE_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
job_type: jobType
})
});

if (response.status === 409) {
showError('A job is already running');
return;
}

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

await fetchJobs();
hideError();
} catch (error) {
showError('Failed to start job: ' + error.message);
}
}

function displayJobs(jobs) {
const tableBody = document.getElementById('jobsTableBody');
tableBody.innerHTML = '';

jobs.forEach(job => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${job.id}</td>
<td>${job.job_type}</td>
<td><span class="status-badge status-${job.status.toLowerCase()}">${job.status}</span></td>
<td>${formatDate(job.created_at)}</td>
<td>${job.completed_at ? formatDate(job.completed_at) : '-'}</td>
`;
tableBody.appendChild(row);
});
}

function formatDate(dateString) {
return new Date(dateString).toLocaleString();
}

function showError(message) {
const errorElement = document.getElementById('errorMessage');
errorElement.textContent = message;
errorElement.style.display = 'block';
}

function hideError() {
const errorElement = document.getElementById('errorMessage');
errorElement.style.display = 'none';
}

// Event Listeners
document.getElementById('startJob').addEventListener('click', startJob);
document.getElementById('refreshJobs').addEventListener('click', fetchJobs);

// Initial load
fetchJobs();

// Auto-refresh every 30 seconds
setInterval(fetchJobs, 30000);
</script>
</body>
</html>

0 comments on commit 6d4668c

Please sign in to comment.