348 lines
9.0 KiB
TypeScript
348 lines
9.0 KiB
TypeScript
/**
|
|
* Automation API Service
|
|
*/
|
|
import { fetchAPI } from './api';
|
|
|
|
export interface AutomationConfig {
|
|
is_enabled: boolean;
|
|
frequency: 'daily' | 'weekly' | 'monthly';
|
|
scheduled_time: string;
|
|
stage_1_enabled: boolean;
|
|
stage_2_enabled: boolean;
|
|
stage_3_enabled: boolean;
|
|
stage_4_enabled: boolean;
|
|
stage_5_enabled: boolean;
|
|
stage_6_enabled: boolean;
|
|
stage_7_enabled: boolean;
|
|
stage_1_batch_size: number;
|
|
stage_2_batch_size: number;
|
|
stage_3_batch_size: number;
|
|
stage_4_batch_size: number;
|
|
stage_5_batch_size: number;
|
|
stage_6_batch_size: number;
|
|
within_stage_delay: number;
|
|
between_stage_delay: number;
|
|
last_run_at: string | null;
|
|
next_run_at: string | null;
|
|
}
|
|
|
|
export interface StageResult {
|
|
[key: string]: any;
|
|
}
|
|
|
|
export interface AutomationRun {
|
|
run_id: string;
|
|
status: 'running' | 'paused' | 'cancelled' | 'completed' | 'failed';
|
|
current_stage: number;
|
|
trigger_type: 'manual' | 'scheduled';
|
|
started_at: string;
|
|
completed_at?: string | null;
|
|
paused_at?: string | null;
|
|
resumed_at?: string | null;
|
|
cancelled_at?: string | null;
|
|
total_credits_used: number;
|
|
stage_1_result: StageResult | null;
|
|
stage_2_result: StageResult | null;
|
|
stage_3_result: StageResult | null;
|
|
stage_4_result: StageResult | null;
|
|
stage_5_result: StageResult | null;
|
|
stage_6_result: StageResult | null;
|
|
stage_7_result: StageResult | null;
|
|
}
|
|
|
|
export interface RunHistoryItem {
|
|
run_id: string;
|
|
status: string;
|
|
trigger_type: string;
|
|
started_at: string;
|
|
completed_at: string | null;
|
|
total_credits_used: number;
|
|
current_stage: number;
|
|
}
|
|
|
|
export interface PipelineStage {
|
|
number: number;
|
|
name: string;
|
|
pending: number;
|
|
type: 'AI' | 'Local' | 'Manual';
|
|
}
|
|
|
|
export interface ProcessingItem {
|
|
id: number;
|
|
title: string;
|
|
type: string;
|
|
}
|
|
|
|
export interface ProcessingState {
|
|
stage_number: number;
|
|
stage_name: string;
|
|
stage_type: 'AI' | 'Local' | 'Manual';
|
|
total_items: number;
|
|
processed_items: number;
|
|
percentage: number;
|
|
currently_processing: ProcessingItem[];
|
|
up_next: ProcessingItem[];
|
|
remaining_count: number;
|
|
}
|
|
|
|
export interface CurrentProcessingResponse {
|
|
state: ProcessingState | null;
|
|
total_credits_used: number;
|
|
current_stage: number;
|
|
}
|
|
|
|
// NEW: Types for unified run_progress endpoint
|
|
export interface StageProgress {
|
|
number: number;
|
|
name: string;
|
|
type: 'AI' | 'Local' | 'Manual';
|
|
status: 'pending' | 'active' | 'completed' | 'skipped';
|
|
input_count: number;
|
|
output_count: number;
|
|
processed_count: number;
|
|
progress_percentage: number;
|
|
credits_used: number;
|
|
time_elapsed: string;
|
|
currently_processing?: ProcessingItem[];
|
|
up_next?: ProcessingItem[];
|
|
remaining_count?: number;
|
|
}
|
|
|
|
export interface GlobalProgress {
|
|
total_items: number;
|
|
completed_items: number;
|
|
percentage: number;
|
|
current_stage: number;
|
|
total_stages: number;
|
|
}
|
|
|
|
export interface InitialSnapshot {
|
|
stage_1_initial: number;
|
|
stage_2_initial: number;
|
|
stage_3_initial: number;
|
|
stage_4_initial: number;
|
|
stage_5_initial: number;
|
|
stage_6_initial: number;
|
|
stage_7_initial: number;
|
|
total_initial_items: number;
|
|
}
|
|
|
|
export interface RunProgressResponse {
|
|
run: {
|
|
run_id: string;
|
|
status: 'running' | 'paused' | 'cancelled' | 'completed' | 'failed';
|
|
current_stage: number;
|
|
trigger_type: 'manual' | 'scheduled';
|
|
started_at: string;
|
|
completed_at: string | null;
|
|
paused_at: string | null;
|
|
} | null;
|
|
global_progress: GlobalProgress | null;
|
|
stages: StageProgress[];
|
|
metrics: {
|
|
credits_used: number;
|
|
duration_seconds: number;
|
|
errors: string[];
|
|
} | null;
|
|
initial_snapshot: InitialSnapshot | null;
|
|
}
|
|
|
|
function buildUrl(endpoint: string, params?: Record<string, any>): string {
|
|
let url = `/v1/automation${endpoint}`;
|
|
if (params) {
|
|
const query = new URLSearchParams(
|
|
Object.entries(params)
|
|
.filter(([, v]) => v != null)
|
|
.map(([k, v]) => [k, String(v)])
|
|
);
|
|
const queryStr = query.toString();
|
|
if (queryStr) {
|
|
url += `?${queryStr}`;
|
|
}
|
|
}
|
|
return url;
|
|
}
|
|
|
|
export const automationService = {
|
|
/**
|
|
* Get automation configuration for site
|
|
*/
|
|
getConfig: async (siteId: number): Promise<AutomationConfig> => {
|
|
return fetchAPI(buildUrl('/config/', { site_id: siteId }));
|
|
},
|
|
|
|
/**
|
|
* Update automation configuration
|
|
*/
|
|
updateConfig: async (siteId: number, config: Partial<AutomationConfig>): Promise<void> => {
|
|
await fetchAPI(buildUrl('/update_config/', { site_id: siteId }), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(config),
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Trigger automation run now
|
|
*/
|
|
runNow: async (siteId: number): Promise<{ run_id: string; message: string }> => {
|
|
return fetchAPI(buildUrl('/run_now/', { site_id: siteId }), {
|
|
method: 'POST',
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get current automation run status
|
|
*/
|
|
getCurrentRun: async (siteId: number): Promise<{ run: AutomationRun | null }> => {
|
|
return fetchAPI(buildUrl('/current_run/', { site_id: siteId }));
|
|
},
|
|
|
|
/**
|
|
* Pause automation run
|
|
*/
|
|
pause: async (siteId: number, runId: string): Promise<void> => {
|
|
await fetchAPI(buildUrl('/pause/', { site_id: siteId, run_id: runId }), {
|
|
method: 'POST',
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Resume paused automation run
|
|
*/
|
|
resume: async (siteId: number, runId: string): Promise<void> => {
|
|
await fetchAPI(buildUrl('/resume/', { site_id: siteId, run_id: runId }), {
|
|
method: 'POST',
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Cancel automation run
|
|
*/
|
|
cancel: async (siteId: number, runId: string): Promise<void> => {
|
|
await fetchAPI(buildUrl('/cancel/', { site_id: siteId, run_id: runId }), {
|
|
method: 'POST',
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get automation run history
|
|
*/
|
|
getHistory: async (siteId: number): Promise<RunHistoryItem[]> => {
|
|
const response = await fetchAPI(buildUrl('/history/', { site_id: siteId }));
|
|
return response.runs;
|
|
},
|
|
|
|
/**
|
|
* Get enhanced automation run history with pagination
|
|
*/
|
|
getEnhancedHistory: async (
|
|
siteId: number,
|
|
page: number = 1,
|
|
pageSize: number = 20
|
|
): Promise<import('../types/automation').HistoryResponse> => {
|
|
return fetchAPI(buildUrl('/history/', { site_id: siteId, page, page_size: pageSize }));
|
|
},
|
|
|
|
/**
|
|
* Get overview statistics with predictive analysis
|
|
*/
|
|
getOverviewStats: async (siteId: number): Promise<import('../types/automation').OverviewStatsResponse> => {
|
|
return fetchAPI(buildUrl('/overview_stats/', { site_id: siteId }));
|
|
},
|
|
|
|
/**
|
|
* Get detailed information about a specific run
|
|
*/
|
|
getRunDetail: async (
|
|
siteId: number,
|
|
runId: string
|
|
): Promise<import('../types/automation').RunDetailResponse> => {
|
|
return fetchAPI(buildUrl('/run_detail/', { site_id: siteId, run_id: runId }));
|
|
},
|
|
|
|
/**
|
|
* Get automation run logs
|
|
*/
|
|
getLogs: async (runId: string, lines: number = 100): Promise<string> => {
|
|
const response = await fetchAPI(buildUrl('/logs/', { run_id: runId, lines }));
|
|
return response.log;
|
|
},
|
|
|
|
/**
|
|
* Estimate credits needed
|
|
*/
|
|
estimate: async (siteId: number): Promise<{
|
|
estimated_credits: number;
|
|
current_balance: number;
|
|
sufficient: boolean;
|
|
}> => {
|
|
return fetchAPI(buildUrl('/estimate/', { site_id: siteId }));
|
|
},
|
|
|
|
/**
|
|
* Get pipeline overview with pending counts for all stages
|
|
*/
|
|
getPipelineOverview: async (siteId: number): Promise<{ stages: PipelineStage[] }> => {
|
|
return fetchAPI(buildUrl('/pipeline_overview/', { site_id: siteId }));
|
|
},
|
|
|
|
/**
|
|
* Publish all content without review (bulk action)
|
|
* Note: backend must implement this endpoint for it to succeed.
|
|
*/
|
|
publishWithoutReview: async (siteId: number): Promise<void> => {
|
|
await fetchAPI(buildUrl('/publish_without_review/', { site_id: siteId }), {
|
|
method: 'POST',
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get current processing state for active automation run
|
|
* Returns state with total_credits_used for real-time credits tracking
|
|
*/
|
|
getCurrentProcessing: async (
|
|
siteId: number,
|
|
runId: string
|
|
): Promise<CurrentProcessingResponse | null> => {
|
|
const response = await fetchAPI(
|
|
buildUrl('/current_processing/', { site_id: siteId, run_id: runId })
|
|
);
|
|
return response.data;
|
|
},
|
|
|
|
/**
|
|
* Get unified run progress data - global + per-stage.
|
|
* This is the recommended endpoint for getting all automation progress data in a single call.
|
|
*/
|
|
getRunProgress: async (
|
|
siteId: number,
|
|
runId?: string
|
|
): Promise<RunProgressResponse> => {
|
|
const params: Record<string, any> = { site_id: siteId };
|
|
if (runId) {
|
|
params.run_id = runId;
|
|
}
|
|
return fetchAPI(buildUrl('/run_progress/', params));
|
|
},
|
|
|
|
/**
|
|
* Check if site is eligible for automation
|
|
* A site is eligible if it has any data in the pipeline (keywords, clusters, ideas, etc.)
|
|
*/
|
|
checkEligibility: async (siteId: number): Promise<{
|
|
is_eligible: boolean;
|
|
totals: {
|
|
keywords: number;
|
|
clusters: number;
|
|
ideas: number;
|
|
tasks: number;
|
|
content: number;
|
|
images: number;
|
|
};
|
|
total_items: number;
|
|
message: string | null;
|
|
}> => {
|
|
return fetchAPI(buildUrl('/eligibility/', { site_id: siteId }));
|
|
},
|
|
};
|