268 lines
6.9 KiB
Rust
268 lines
6.9 KiB
Rust
//! Service layer for persistent storage of keys, runners, and jobs
|
|
//!
|
|
//! This module provides database/storage services for the supervisor.
|
|
//! Currently uses in-memory storage, but designed to be easily extended
|
|
//! to use Redis, PostgreSQL, or other persistent storage backends.
|
|
|
|
use crate::auth::{ApiKey, ApiKeyScope};
|
|
use hero_job::Job;
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use tokio::sync::Mutex;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Service for managing API keys
|
|
#[derive(Debug, Clone)]
|
|
pub struct ApiKeyService {
|
|
store: Arc<Mutex<HashMap<String, ApiKey>>>,
|
|
}
|
|
|
|
impl ApiKeyService {
|
|
/// Create a new API key service
|
|
pub fn new() -> Self {
|
|
Self {
|
|
store: Arc::new(Mutex::new(HashMap::new())),
|
|
}
|
|
}
|
|
|
|
/// Store an API key
|
|
pub async fn store(&self, key: ApiKey) -> Result<(), String> {
|
|
let mut store = self.store.lock().await;
|
|
store.insert(key.key.clone(), key);
|
|
Ok(())
|
|
}
|
|
|
|
/// Get an API key by its key string
|
|
pub async fn get(&self, key: &str) -> Option<ApiKey> {
|
|
let store = self.store.lock().await;
|
|
store.get(key).cloned()
|
|
}
|
|
|
|
/// List all API keys
|
|
pub async fn list(&self) -> Vec<ApiKey> {
|
|
let store = self.store.lock().await;
|
|
store.values().cloned().collect()
|
|
}
|
|
|
|
/// Remove an API key
|
|
pub async fn remove(&self, key: &str) -> Option<ApiKey> {
|
|
let mut store = self.store.lock().await;
|
|
store.remove(key)
|
|
}
|
|
|
|
/// Count API keys by scope
|
|
pub async fn count_by_scope(&self, scope: ApiKeyScope) -> usize {
|
|
let store = self.store.lock().await;
|
|
store.values().filter(|k| k.scope == scope).count()
|
|
}
|
|
|
|
/// Clear all API keys (for testing)
|
|
pub async fn clear(&self) {
|
|
let mut store = self.store.lock().await;
|
|
store.clear();
|
|
}
|
|
}
|
|
|
|
impl Default for ApiKeyService {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// Service for managing runners
|
|
#[derive(Debug, Clone)]
|
|
pub struct RunnerService {
|
|
store: Arc<Mutex<HashMap<String, RunnerMetadata>>>,
|
|
}
|
|
|
|
/// Metadata about a runner for storage
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct RunnerMetadata {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub queue: String,
|
|
pub registered_at: String,
|
|
pub registered_by: String, // API key name that registered this runner
|
|
}
|
|
|
|
impl RunnerService {
|
|
/// Create a new runner service
|
|
pub fn new() -> Self {
|
|
Self {
|
|
store: Arc::new(Mutex::new(HashMap::new())),
|
|
}
|
|
}
|
|
|
|
/// Store runner metadata
|
|
pub async fn store(&self, metadata: RunnerMetadata) -> Result<(), String> {
|
|
let mut store = self.store.lock().await;
|
|
store.insert(metadata.id.clone(), metadata);
|
|
Ok(())
|
|
}
|
|
|
|
/// Get runner metadata by ID
|
|
pub async fn get(&self, id: &str) -> Option<RunnerMetadata> {
|
|
let store = self.store.lock().await;
|
|
store.get(id).cloned()
|
|
}
|
|
|
|
/// List all runners
|
|
pub async fn list(&self) -> Vec<RunnerMetadata> {
|
|
let store = self.store.lock().await;
|
|
store.values().cloned().collect()
|
|
}
|
|
|
|
/// Remove a runner
|
|
pub async fn remove(&self, id: &str) -> Option<RunnerMetadata> {
|
|
let mut store = self.store.lock().await;
|
|
store.remove(id)
|
|
}
|
|
|
|
/// Count total runners
|
|
pub async fn count(&self) -> usize {
|
|
let store = self.store.lock().await;
|
|
store.len()
|
|
}
|
|
|
|
/// Clear all runners (for testing)
|
|
pub async fn clear(&self) {
|
|
let mut store = self.store.lock().await;
|
|
store.clear();
|
|
}
|
|
}
|
|
|
|
impl Default for RunnerService {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// Service for managing jobs
|
|
#[derive(Debug, Clone)]
|
|
pub struct JobService {
|
|
store: Arc<Mutex<HashMap<String, JobMetadata>>>,
|
|
}
|
|
|
|
/// Metadata about a job for storage
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct JobMetadata {
|
|
pub job_id: String,
|
|
pub runner: String,
|
|
pub created_at: String,
|
|
pub created_by: String, // API key name that created this job
|
|
pub status: String,
|
|
pub job: Job,
|
|
}
|
|
|
|
impl JobService {
|
|
/// Create a new job service
|
|
pub fn new() -> Self {
|
|
Self {
|
|
store: Arc::new(Mutex::new(HashMap::new())),
|
|
}
|
|
}
|
|
|
|
/// Store job metadata
|
|
pub async fn store(&self, metadata: JobMetadata) -> Result<(), String> {
|
|
let mut store = self.store.lock().await;
|
|
store.insert(metadata.job_id.clone(), metadata);
|
|
Ok(())
|
|
}
|
|
|
|
/// Get job metadata by ID
|
|
pub async fn get(&self, job_id: &str) -> Option<JobMetadata> {
|
|
let store = self.store.lock().await;
|
|
store.get(job_id).cloned()
|
|
}
|
|
|
|
/// List all jobs
|
|
pub async fn list(&self) -> Vec<JobMetadata> {
|
|
let store = self.store.lock().await;
|
|
store.values().cloned().collect()
|
|
}
|
|
|
|
/// List jobs by runner
|
|
pub async fn list_by_runner(&self, runner: &str) -> Vec<JobMetadata> {
|
|
let store = self.store.lock().await;
|
|
store.values()
|
|
.filter(|j| j.runner == runner)
|
|
.cloned()
|
|
.collect()
|
|
}
|
|
|
|
/// List jobs by creator (API key name)
|
|
pub async fn list_by_creator(&self, creator: &str) -> Vec<JobMetadata> {
|
|
let store = self.store.lock().await;
|
|
store.values()
|
|
.filter(|j| j.created_by == creator)
|
|
.cloned()
|
|
.collect()
|
|
}
|
|
|
|
/// Update job status
|
|
pub async fn update_status(&self, job_id: &str, status: String) -> Result<(), String> {
|
|
let mut store = self.store.lock().await;
|
|
if let Some(metadata) = store.get_mut(job_id) {
|
|
metadata.status = status;
|
|
Ok(())
|
|
} else {
|
|
Err(format!("Job not found: {}", job_id))
|
|
}
|
|
}
|
|
|
|
/// Remove a job
|
|
pub async fn remove(&self, job_id: &str) -> Option<JobMetadata> {
|
|
let mut store = self.store.lock().await;
|
|
store.remove(job_id)
|
|
}
|
|
|
|
/// Count total jobs
|
|
pub async fn count(&self) -> usize {
|
|
let store = self.store.lock().await;
|
|
store.len()
|
|
}
|
|
|
|
/// Clear all jobs (for testing)
|
|
pub async fn clear(&self) {
|
|
let mut store = self.store.lock().await;
|
|
store.clear();
|
|
}
|
|
}
|
|
|
|
impl Default for JobService {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// Combined service container for all storage services
|
|
#[derive(Debug, Clone)]
|
|
pub struct Services {
|
|
pub api_keys: ApiKeyService,
|
|
pub runners: RunnerService,
|
|
pub jobs: JobService,
|
|
}
|
|
|
|
impl Services {
|
|
/// Create a new services container
|
|
pub fn new() -> Self {
|
|
Self {
|
|
api_keys: ApiKeyService::new(),
|
|
runners: RunnerService::new(),
|
|
jobs: JobService::new(),
|
|
}
|
|
}
|
|
|
|
/// Clear all data (for testing)
|
|
pub async fn clear_all(&self) {
|
|
self.api_keys.clear().await;
|
|
self.runners.clear().await;
|
|
self.jobs.clear().await;
|
|
}
|
|
}
|
|
|
|
impl Default for Services {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
} |