Implement comprehensive admin UI with job management and API key display
Admin UI Features:
- Complete job lifecycle: create, run, view status, view output, delete
- Job table with sorting, filtering, and real-time status updates
- Status polling with countdown timers for running jobs
- Job output modal with result/error display
- API keys management: create keys, list keys with secrets visible
- Sidebar toggle between runners and keys views
- Toast notifications for errors
- Modern dark theme UI with responsive design
Supervisor Improvements:
- Fixed job status persistence using client methods
- Refactored get_job_result to use client.get_status, get_result, get_error
- Changed runner_rust dependency from git to local path
- Authentication system with API key scopes (admin, user, register)
- Job listing with status fetching from Redis
- Services module for job and auth operations
OpenRPC Client:
- Added auth_list_keys method for fetching API keys
- WASM bindings for browser usage
- Proper error handling and type conversions
Build Status: ✅ All components build successfully
			
			
This commit is contained in:
		
							
								
								
									
										262
									
								
								src/openrpc.rs
									
									
									
									
									
								
							
							
						
						
									
										262
									
								
								src/openrpc.rs
									
									
									
									
									
								
							@@ -13,7 +13,7 @@ use log::{debug, info, error};
 | 
			
		||||
 | 
			
		||||
use crate::supervisor::Supervisor;
 | 
			
		||||
use crate::runner::{Runner, RunnerError};
 | 
			
		||||
use crate::runner::{ProcessManagerError, ProcessStatus, LogInfo};
 | 
			
		||||
use crate::runner::{ProcessStatus, LogInfo};
 | 
			
		||||
use crate::job::Job;
 | 
			
		||||
use crate::ProcessManagerType;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
@@ -69,12 +69,10 @@ fn invalid_params_error(msg: &str) -> ErrorObject<'static> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for registering a new runner
 | 
			
		||||
/// TODO: Move secret to HTTP Authorization header for better security
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
/// The secret is extracted from Authorization header
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize, Clone)]
 | 
			
		||||
pub struct RegisterRunnerParams {
 | 
			
		||||
    pub secret: String,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    // Note: queue is derived from name (name = queue)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for runner management operations
 | 
			
		||||
@@ -112,17 +110,15 @@ pub struct RunnerConfig {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for running a job
 | 
			
		||||
/// TODO: Move secret to HTTP Authorization header for better security
 | 
			
		||||
/// The secret is extracted from Authorization header
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct RunJobParams {
 | 
			
		||||
    pub secret: String,
 | 
			
		||||
    pub job: Job,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for starting a job
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct StartJobParams {
 | 
			
		||||
    pub secret: String,
 | 
			
		||||
    pub job_id: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -139,9 +135,6 @@ pub enum JobResult {
 | 
			
		||||
pub struct JobStatusResponse {
 | 
			
		||||
    pub job_id: String,
 | 
			
		||||
    pub status: String,
 | 
			
		||||
    pub created_at: String,
 | 
			
		||||
    pub started_at: Option<String>,
 | 
			
		||||
    pub completed_at: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for queuing a job
 | 
			
		||||
@@ -169,7 +162,6 @@ pub struct StopJobParams {
 | 
			
		||||
/// Request parameters for deleting a job
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct DeleteJobParams {
 | 
			
		||||
    pub secret: String,
 | 
			
		||||
    pub job_id: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -257,6 +249,23 @@ pub struct LogInfoWrapper {
 | 
			
		||||
    pub message: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Thread-local storage for the current request's API key
 | 
			
		||||
thread_local! {
 | 
			
		||||
    static CURRENT_API_KEY: std::cell::RefCell<Option<String>> = std::cell::RefCell::new(None);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set the current API key for this request
 | 
			
		||||
pub fn set_current_api_key(key: Option<String>) {
 | 
			
		||||
    CURRENT_API_KEY.with(|k| {
 | 
			
		||||
        *k.borrow_mut() = key;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the current API key for this request
 | 
			
		||||
pub fn get_current_api_key() -> Option<String> {
 | 
			
		||||
    CURRENT_API_KEY.with(|k| k.borrow().clone())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<LogInfo> for LogInfoWrapper {
 | 
			
		||||
    fn from(log: crate::runner::LogInfo) -> Self {
 | 
			
		||||
        LogInfoWrapper {
 | 
			
		||||
@@ -284,12 +293,34 @@ pub struct SupervisorInfoResponse {
 | 
			
		||||
    pub runners_count: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for auth verification
 | 
			
		||||
/// Empty - the key is extracted from Authorization header
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize, Default)]
 | 
			
		||||
pub struct AuthVerifyParams {}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for creating API keys
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct CreateApiKeyParams {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub scope: String, // "admin", "registrar", or "user"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for removing API keys
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct RemoveApiKeyParams {
 | 
			
		||||
    pub key: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request parameters for listing API keys - empty, uses header auth
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize, Default)]
 | 
			
		||||
pub struct ListApiKeysParams {}
 | 
			
		||||
 | 
			
		||||
/// OpenRPC trait defining all supervisor methods
 | 
			
		||||
#[rpc(server)]
 | 
			
		||||
pub trait SupervisorRpc {
 | 
			
		||||
    /// Register a new runner with secret-based authentication
 | 
			
		||||
    #[method(name = "register_runner")]
 | 
			
		||||
    async fn register_runner(&self, params: RegisterRunnerParams) -> RpcResult<String>;
 | 
			
		||||
    async fn register_runner(&self, name: String) -> RpcResult<String>;
 | 
			
		||||
 | 
			
		||||
    /// Create a job without queuing it to a runner
 | 
			
		||||
    #[method(name = "jobs.create")]
 | 
			
		||||
@@ -423,6 +454,22 @@ pub trait SupervisorRpc {
 | 
			
		||||
    #[method(name = "get_supervisor_info")]
 | 
			
		||||
    async fn get_supervisor_info(&self, admin_secret: String) -> RpcResult<SupervisorInfoResponse>;
 | 
			
		||||
    
 | 
			
		||||
    /// Verify an API key and return its metadata
 | 
			
		||||
    #[method(name = "auth.verify")]
 | 
			
		||||
    async fn auth_verify(&self) -> RpcResult<crate::auth::AuthVerifyResponse>;
 | 
			
		||||
    
 | 
			
		||||
    /// Create a new API key (admin only)
 | 
			
		||||
    #[method(name = "auth.create_key")]
 | 
			
		||||
    async fn auth_create_key(&self, name: String, scope: String) -> RpcResult<crate::auth::ApiKey>;
 | 
			
		||||
    
 | 
			
		||||
    /// Remove an API key (admin only)
 | 
			
		||||
    #[method(name = "auth.remove_key")]
 | 
			
		||||
    async fn auth_remove_key(&self, key: String) -> RpcResult<bool>;
 | 
			
		||||
    
 | 
			
		||||
    /// List all API keys (admin only)
 | 
			
		||||
    #[method(name = "auth.list_keys")]
 | 
			
		||||
    async fn auth_list_keys(&self) -> RpcResult<Vec<crate::auth::ApiKey>>;
 | 
			
		||||
    
 | 
			
		||||
    /// OpenRPC discovery method - returns the OpenRPC document describing this API
 | 
			
		||||
    #[method(name = "rpc.discover")]
 | 
			
		||||
    async fn rpc_discover(&self) -> RpcResult<serde_json::Value>;
 | 
			
		||||
@@ -447,26 +494,35 @@ fn parse_process_manager_type(pm_type: &str, session_name: Option<String>) -> Re
 | 
			
		||||
/// This eliminates the need for a wrapper struct
 | 
			
		||||
#[async_trait]
 | 
			
		||||
impl SupervisorRpcServer for Arc<Mutex<Supervisor>> {
 | 
			
		||||
    async fn register_runner(&self, params: RegisterRunnerParams) -> RpcResult<String> {
 | 
			
		||||
        debug!("OpenRPC request: register_runner with params: {:?}", params);
 | 
			
		||||
    async fn register_runner(&self, name: String) -> RpcResult<String> {
 | 
			
		||||
        debug!("OpenRPC request: register_runner with name: {}", name);
 | 
			
		||||
        
 | 
			
		||||
        // Get API key from Authorization header
 | 
			
		||||
        let key = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        let mut supervisor = self.lock().await;
 | 
			
		||||
        // Queue name is the same as runner name
 | 
			
		||||
        
 | 
			
		||||
        // register_runner now handles API key verification internally
 | 
			
		||||
        supervisor
 | 
			
		||||
            .register_runner(¶ms.secret, ¶ms.name, ¶ms.name)
 | 
			
		||||
            .register_runner(&key, &name, &name)
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(runner_error_to_rpc_error)?;
 | 
			
		||||
        
 | 
			
		||||
        // Return the runner name that was registered
 | 
			
		||||
        Ok(params.name)
 | 
			
		||||
        Ok(name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn jobs_create(&self, params: RunJobParams) -> RpcResult<String> {
 | 
			
		||||
        debug!("OpenRPC request: jobs.create with params: {:?}", params);
 | 
			
		||||
        
 | 
			
		||||
        // Get secret from Authorization header
 | 
			
		||||
        let secret = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        let mut supervisor = self.lock().await;
 | 
			
		||||
        let job_id = supervisor
 | 
			
		||||
            .create_job(¶ms.secret, params.job)
 | 
			
		||||
            .create_job(&secret, params.job)
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(runner_error_to_rpc_error)?;
 | 
			
		||||
        
 | 
			
		||||
@@ -485,9 +541,13 @@ impl SupervisorRpcServer for Arc<Mutex<Supervisor>> {
 | 
			
		||||
    async fn job_run(&self, params: RunJobParams) -> RpcResult<JobResult> {
 | 
			
		||||
        debug!("OpenRPC request: job.run with params: {:?}", params);
 | 
			
		||||
        
 | 
			
		||||
        // Get secret from Authorization header
 | 
			
		||||
        let secret = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        let mut supervisor = self.lock().await;
 | 
			
		||||
        match supervisor
 | 
			
		||||
            .run_job(¶ms.secret, params.job)
 | 
			
		||||
            .run_job(&secret, params.job)
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(runner_error_to_rpc_error)? {
 | 
			
		||||
            Some(output) => Ok(JobResult::Success { success: output }),
 | 
			
		||||
@@ -498,9 +558,13 @@ impl SupervisorRpcServer for Arc<Mutex<Supervisor>> {
 | 
			
		||||
    async fn job_start(&self, params: StartJobParams) -> RpcResult<()> {
 | 
			
		||||
        debug!("OpenRPC request: job.start with params: {:?}", params);
 | 
			
		||||
        
 | 
			
		||||
        // Get secret from Authorization header
 | 
			
		||||
        let secret = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        let mut supervisor = self.lock().await;
 | 
			
		||||
        supervisor
 | 
			
		||||
            .start_job(¶ms.secret, ¶ms.job_id)
 | 
			
		||||
            .start_job(&secret, ¶ms.job_id)
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(runner_error_to_rpc_error)
 | 
			
		||||
    }
 | 
			
		||||
@@ -549,9 +613,13 @@ impl SupervisorRpcServer for Arc<Mutex<Supervisor>> {
 | 
			
		||||
    async fn job_delete(&self, params: DeleteJobParams) -> RpcResult<()> {
 | 
			
		||||
        debug!("OpenRPC request: job.delete with params: {:?}", params);
 | 
			
		||||
        
 | 
			
		||||
        // Get secret from Authorization header
 | 
			
		||||
        let secret = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        let mut supervisor = self.lock().await;
 | 
			
		||||
        supervisor
 | 
			
		||||
            .delete_job(¶ms.job_id)
 | 
			
		||||
            .delete_job_with_auth(&secret, ¶ms.job_id)
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(runner_error_to_rpc_error)
 | 
			
		||||
    }
 | 
			
		||||
@@ -875,6 +943,92 @@ impl SupervisorRpcServer for Arc<Mutex<Supervisor>> {
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    async fn auth_verify(&self) -> RpcResult<crate::auth::AuthVerifyResponse> {
 | 
			
		||||
        debug!("OpenRPC request: auth.verify");
 | 
			
		||||
        let supervisor = self.lock().await;
 | 
			
		||||
        
 | 
			
		||||
        // Get key from thread-local (set by middleware from Authorization header)
 | 
			
		||||
        let key = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        match supervisor.verify_api_key(&key).await {
 | 
			
		||||
            Some(api_key) => {
 | 
			
		||||
                Ok(crate::auth::AuthVerifyResponse {
 | 
			
		||||
                    valid: true,
 | 
			
		||||
                    name: api_key.name,
 | 
			
		||||
                    scope: api_key.scope.as_str().to_string(),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                Ok(crate::auth::AuthVerifyResponse {
 | 
			
		||||
                    valid: false,
 | 
			
		||||
                    name: String::new(),
 | 
			
		||||
                    scope: String::new(),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    async fn auth_create_key(&self, name: String, scope: String) -> RpcResult<crate::auth::ApiKey> {
 | 
			
		||||
        debug!("OpenRPC request: auth.create_key");
 | 
			
		||||
        
 | 
			
		||||
        // Get API key from Authorization header
 | 
			
		||||
        let key = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        let supervisor = self.lock().await;
 | 
			
		||||
        
 | 
			
		||||
        // Verify admin key
 | 
			
		||||
        if !supervisor.is_admin_key(&key).await {
 | 
			
		||||
            return Err(ErrorObject::owned(-32603, "Admin permissions required", None::<()>));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Parse scope
 | 
			
		||||
        let api_scope = match scope.to_lowercase().as_str() {
 | 
			
		||||
            "admin" => crate::auth::ApiKeyScope::Admin,
 | 
			
		||||
            "registrar" => crate::auth::ApiKeyScope::Registrar,
 | 
			
		||||
            "user" => crate::auth::ApiKeyScope::User,
 | 
			
		||||
            _ => return Err(ErrorObject::owned(-32602, "Invalid scope. Must be 'admin', 'registrar', or 'user'", None::<()>)),
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let api_key = supervisor.create_api_key(name, api_scope).await;
 | 
			
		||||
        Ok(api_key)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    async fn auth_remove_key(&self, key_to_remove: String) -> RpcResult<bool> {
 | 
			
		||||
        debug!("OpenRPC request: auth.remove_key");
 | 
			
		||||
        
 | 
			
		||||
        // Get API key from Authorization header
 | 
			
		||||
        let key = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        let supervisor = self.lock().await;
 | 
			
		||||
        
 | 
			
		||||
        // Verify admin key
 | 
			
		||||
        if !supervisor.is_admin_key(&key).await {
 | 
			
		||||
            return Err(ErrorObject::owned(-32603, "Admin permissions required", None::<()>));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(supervisor.remove_api_key(&key_to_remove).await.is_some())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    async fn auth_list_keys(&self) -> RpcResult<Vec<crate::auth::ApiKey>> {
 | 
			
		||||
        debug!("OpenRPC request: auth.list_keys");
 | 
			
		||||
        
 | 
			
		||||
        // Get API key from Authorization header
 | 
			
		||||
        let key = get_current_api_key()
 | 
			
		||||
            .ok_or_else(|| ErrorObject::owned(-32602, "Missing Authorization header", None::<()>))?;
 | 
			
		||||
        
 | 
			
		||||
        let supervisor = self.lock().await;
 | 
			
		||||
        
 | 
			
		||||
        // Verify admin key
 | 
			
		||||
        if !supervisor.is_admin_key(&key).await {
 | 
			
		||||
            return Err(ErrorObject::owned(-32603, "Admin permissions required", None::<()>));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(supervisor.list_api_keys().await)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    async fn rpc_discover(&self) -> RpcResult<serde_json::Value> {
 | 
			
		||||
        debug!("OpenRPC request: rpc.discover");
 | 
			
		||||
        
 | 
			
		||||
@@ -915,6 +1069,55 @@ pub async fn start_server_with_supervisor(
 | 
			
		||||
    Ok(handle)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// HTTP middleware layer to extract Authorization header
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct AuthExtractLayer;
 | 
			
		||||
 | 
			
		||||
impl<S> tower::Layer<S> for AuthExtractLayer {
 | 
			
		||||
    type Service = AuthExtractService<S>;
 | 
			
		||||
 | 
			
		||||
    fn layer(&self, inner: S) -> Self::Service {
 | 
			
		||||
        AuthExtractService { inner }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct AuthExtractService<S> {
 | 
			
		||||
    inner: S,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<S, B> tower::Service<hyper::Request<B>> for AuthExtractService<S>
 | 
			
		||||
where
 | 
			
		||||
    S: tower::Service<hyper::Request<B>> + Clone + Send + 'static,
 | 
			
		||||
    S::Future: Send + 'static,
 | 
			
		||||
    B: Send + 'static,
 | 
			
		||||
{
 | 
			
		||||
    type Response = S::Response;
 | 
			
		||||
    type Error = S::Error;
 | 
			
		||||
    type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send>>;
 | 
			
		||||
 | 
			
		||||
    fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
 | 
			
		||||
        self.inner.poll_ready(cx)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn call(&mut self, req: hyper::Request<B>) -> Self::Future {
 | 
			
		||||
        // Extract Authorization header
 | 
			
		||||
        let api_key = req.headers()
 | 
			
		||||
            .get("authorization")
 | 
			
		||||
            .and_then(|h| h.to_str().ok())
 | 
			
		||||
            .and_then(|h| h.strip_prefix("Bearer "))
 | 
			
		||||
            .map(|s| s.to_string());
 | 
			
		||||
        
 | 
			
		||||
        // Store in thread-local
 | 
			
		||||
        set_current_api_key(api_key);
 | 
			
		||||
        
 | 
			
		||||
        let mut inner = self.inner.clone();
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            inner.call(req).await
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start HTTP OpenRPC server (Unix socket support would require additional dependencies)
 | 
			
		||||
pub async fn start_http_openrpc_server(
 | 
			
		||||
    supervisor: Arc<Mutex<Supervisor>>,
 | 
			
		||||
@@ -929,9 +1132,14 @@ pub async fn start_http_openrpc_server(
 | 
			
		||||
        .allow_headers(Any)
 | 
			
		||||
        .allow_methods(Any);
 | 
			
		||||
    
 | 
			
		||||
    // Start HTTP server with CORS
 | 
			
		||||
    // Build HTTP middleware stack with auth extraction
 | 
			
		||||
    let http_middleware = tower::ServiceBuilder::new()
 | 
			
		||||
        .layer(AuthExtractLayer)
 | 
			
		||||
        .layer(cors);
 | 
			
		||||
    
 | 
			
		||||
    // Start HTTP server with middleware
 | 
			
		||||
    let http_server = Server::builder()
 | 
			
		||||
        .set_http_middleware(tower::ServiceBuilder::new().layer(cors))
 | 
			
		||||
        .set_http_middleware(http_middleware)
 | 
			
		||||
        .build(http_addr)
 | 
			
		||||
        .await?;
 | 
			
		||||
    let http_handle = http_server.start(supervisor.into_rpc());
 | 
			
		||||
@@ -1015,9 +1223,11 @@ mod tests {
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let params = RunJobParams {
 | 
			
		||||
            secret: "test-secret".to_string(),
 | 
			
		||||
            job: job.clone(),
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        // Set the API key in thread-local for the test
 | 
			
		||||
        set_current_api_key(Some("test-secret".to_string()));
 | 
			
		||||
 | 
			
		||||
        let result = supervisor.jobs_create(params).await;
 | 
			
		||||
        // Should work or fail gracefully without Redis
 | 
			
		||||
@@ -1025,7 +1235,6 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
        // Test job.start
 | 
			
		||||
        let start_params = StartJobParams {
 | 
			
		||||
            secret: "test-secret".to_string(),
 | 
			
		||||
            job_id: "test-job".to_string(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -1035,7 +1244,6 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
        // Test invalid secret
 | 
			
		||||
        let invalid_params = StartJobParams {
 | 
			
		||||
            secret: "invalid".to_string(),
 | 
			
		||||
            job_id: "test-job".to_string(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user