mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
feat: Implement GitHub issue validation management and UI enhancements
- Introduced CRUD operations for GitHub issue validation results, including storage and retrieval. - Added new endpoints for checking validation status, stopping validations, and deleting stored validations. - Enhanced the GitHub routes to support validation management features. - Updated the UI to display validation results and manage validation states for GitHub issues. - Integrated event handling for validation progress and completion notifications.
This commit is contained in:
147
apps/server/src/lib/validation-storage.ts
Normal file
147
apps/server/src/lib/validation-storage.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Validation Storage - CRUD operations for GitHub issue validation results
|
||||
*
|
||||
* Stores validation results in .automaker/validations/{issueNumber}/validation.json
|
||||
* Results include the validation verdict, metadata, and timestamp for cache invalidation.
|
||||
*/
|
||||
|
||||
import * as secureFs from './secure-fs.js';
|
||||
import { getValidationsDir, getValidationDir, getValidationPath } from '@automaker/platform';
|
||||
import type { StoredValidation } from '@automaker/types';
|
||||
|
||||
// Re-export StoredValidation for convenience
|
||||
export type { StoredValidation };
|
||||
|
||||
/** Number of hours before a validation is considered stale */
|
||||
const VALIDATION_CACHE_TTL_HOURS = 24;
|
||||
|
||||
/**
|
||||
* Write validation result to storage
|
||||
*
|
||||
* Creates the validation directory if needed and stores the result as JSON.
|
||||
*
|
||||
* @param projectPath - Absolute path to project directory
|
||||
* @param issueNumber - GitHub issue number
|
||||
* @param data - Validation data to store
|
||||
*/
|
||||
export async function writeValidation(
|
||||
projectPath: string,
|
||||
issueNumber: number,
|
||||
data: StoredValidation
|
||||
): Promise<void> {
|
||||
const validationDir = getValidationDir(projectPath, issueNumber);
|
||||
const validationPath = getValidationPath(projectPath, issueNumber);
|
||||
|
||||
// Ensure directory exists
|
||||
await secureFs.mkdir(validationDir, { recursive: true });
|
||||
|
||||
// Write validation result
|
||||
await secureFs.writeFile(validationPath, JSON.stringify(data, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Read validation result from storage
|
||||
*
|
||||
* @param projectPath - Absolute path to project directory
|
||||
* @param issueNumber - GitHub issue number
|
||||
* @returns Stored validation or null if not found
|
||||
*/
|
||||
export async function readValidation(
|
||||
projectPath: string,
|
||||
issueNumber: number
|
||||
): Promise<StoredValidation | null> {
|
||||
try {
|
||||
const validationPath = getValidationPath(projectPath, issueNumber);
|
||||
const content = (await secureFs.readFile(validationPath, 'utf-8')) as string;
|
||||
return JSON.parse(content) as StoredValidation;
|
||||
} catch {
|
||||
// File doesn't exist or can't be read
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all stored validations for a project
|
||||
*
|
||||
* @param projectPath - Absolute path to project directory
|
||||
* @returns Array of stored validations
|
||||
*/
|
||||
export async function getAllValidations(projectPath: string): Promise<StoredValidation[]> {
|
||||
const validations: StoredValidation[] = [];
|
||||
const validationsDir = getValidationsDir(projectPath);
|
||||
|
||||
try {
|
||||
const dirs = await secureFs.readdir(validationsDir, { withFileTypes: true });
|
||||
|
||||
for (const dir of dirs) {
|
||||
if (dir.isDirectory()) {
|
||||
const issueNumber = parseInt(dir.name, 10);
|
||||
if (!isNaN(issueNumber)) {
|
||||
const validation = await readValidation(projectPath, issueNumber);
|
||||
if (validation) {
|
||||
validations.push(validation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Directory doesn't exist
|
||||
}
|
||||
|
||||
// Sort by issue number
|
||||
validations.sort((a, b) => a.issueNumber - b.issueNumber);
|
||||
|
||||
return validations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a validation from storage
|
||||
*
|
||||
* @param projectPath - Absolute path to project directory
|
||||
* @param issueNumber - GitHub issue number
|
||||
* @returns true if validation was deleted, false if not found
|
||||
*/
|
||||
export async function deleteValidation(projectPath: string, issueNumber: number): Promise<boolean> {
|
||||
try {
|
||||
const validationDir = getValidationDir(projectPath, issueNumber);
|
||||
await secureFs.rm(validationDir, { recursive: true, force: true });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a validation is stale (older than TTL)
|
||||
*
|
||||
* @param validation - Stored validation to check
|
||||
* @returns true if validation is older than 24 hours
|
||||
*/
|
||||
export function isValidationStale(validation: StoredValidation): boolean {
|
||||
const validatedAt = new Date(validation.validatedAt);
|
||||
const now = new Date();
|
||||
const hoursDiff = (now.getTime() - validatedAt.getTime()) / (1000 * 60 * 60);
|
||||
return hoursDiff > VALIDATION_CACHE_TTL_HOURS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get validation with freshness info
|
||||
*
|
||||
* @param projectPath - Absolute path to project directory
|
||||
* @param issueNumber - GitHub issue number
|
||||
* @returns Object with validation and isStale flag, or null if not found
|
||||
*/
|
||||
export async function getValidationWithFreshness(
|
||||
projectPath: string,
|
||||
issueNumber: number
|
||||
): Promise<{ validation: StoredValidation; isStale: boolean } | null> {
|
||||
const validation = await readValidation(projectPath, issueNumber);
|
||||
if (!validation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
validation,
|
||||
isStale: isValidationStale(validation),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user