clear db button
This commit is contained in:
parent
f9352ad5b0
commit
a6a4cb9f89
@ -13,6 +13,8 @@
|
|||||||
"build:client": "vite build",
|
"build:client": "vite build",
|
||||||
"start": "node dist/server/index.js",
|
"start": "node dist/server/index.js",
|
||||||
"db:migrate": "tsx src/server/db/migrate.ts",
|
"db:migrate": "tsx src/server/db/migrate.ts",
|
||||||
|
"db:clear": "tsx src/server/db/clear.ts",
|
||||||
|
"db:drop": "tsx src/server/db/clear.ts --drop",
|
||||||
"pipeline:run": "tsx src/server/pipeline/run.ts"
|
"pipeline:run": "tsx src/server/pipeline/run.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -40,4 +42,4 @@
|
|||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"vite": "^6.0.3"
|
"vite": "^6.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,11 +135,24 @@ export const App: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Clear database
|
||||||
|
const handleClearDatabase = async () => {
|
||||||
|
try {
|
||||||
|
const result = await api.clearDatabase();
|
||||||
|
addToast(`Database cleared! Deleted ${result.jobsDeleted} jobs.`, 'success');
|
||||||
|
loadJobs();
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : 'Failed to clear database';
|
||||||
|
addToast(message, 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<Header
|
||||||
onRunPipeline={handleRunPipeline}
|
onRunPipeline={handleRunPipeline}
|
||||||
onRefresh={loadJobs}
|
onRefresh={loadJobs}
|
||||||
|
onClearDatabase={handleClearDatabase}
|
||||||
isPipelineRunning={isPipelineRunning}
|
isPipelineRunning={isPipelineRunning}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -89,3 +89,18 @@ export async function runPipeline(config?: {
|
|||||||
body: JSON.stringify(config || {}),
|
body: JSON.stringify(config || {}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Database API
|
||||||
|
export async function clearDatabase(): Promise<{
|
||||||
|
message: string;
|
||||||
|
jobsDeleted: number;
|
||||||
|
runsDeleted: number;
|
||||||
|
}> {
|
||||||
|
return fetchApi<{
|
||||||
|
message: string;
|
||||||
|
jobsDeleted: number;
|
||||||
|
runsDeleted: number;
|
||||||
|
}>('/database', {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -3,11 +3,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { RocketIcon, PlayIcon, RefreshIcon } from './Icons';
|
import { RocketIcon, PlayIcon, RefreshIcon, TrashIcon } from './Icons';
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
onRunPipeline: () => void;
|
onRunPipeline: () => void;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
|
onClearDatabase: () => void;
|
||||||
isPipelineRunning: boolean;
|
isPipelineRunning: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
@ -15,9 +16,16 @@ interface HeaderProps {
|
|||||||
export const Header: React.FC<HeaderProps> = ({
|
export const Header: React.FC<HeaderProps> = ({
|
||||||
onRunPipeline,
|
onRunPipeline,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
|
onClearDatabase,
|
||||||
isPipelineRunning,
|
isPipelineRunning,
|
||||||
isLoading,
|
isLoading,
|
||||||
}) => {
|
}) => {
|
||||||
|
const handleClearDatabase = () => {
|
||||||
|
if (window.confirm('Are you sure you want to clear all jobs from the database? This cannot be undone.')) {
|
||||||
|
onClearDatabase();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="header">
|
<header className="header">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@ -30,6 +38,16 @@ export const Header: React.FC<HeaderProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
|
<button
|
||||||
|
className="btn btn-ghost"
|
||||||
|
onClick={handleClearDatabase}
|
||||||
|
disabled={isLoading}
|
||||||
|
title="Clear all jobs from database"
|
||||||
|
>
|
||||||
|
<TrashIcon size={16} />
|
||||||
|
Clear DB
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-ghost"
|
className="btn btn-ghost"
|
||||||
onClick={onRefresh}
|
onClick={onRefresh}
|
||||||
|
|||||||
@ -116,3 +116,12 @@ export const RocketIcon: React.FC<IconProps> = ({ className, size = 16 }) => (
|
|||||||
<path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>
|
<path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const TrashIcon: React.FC<IconProps> = ({ className, size = 16 }) => (
|
||||||
|
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<polyline points="3 6 5 6 21 6"/>
|
||||||
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||||
|
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||||
|
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import * as jobsRepo from '../repositories/jobs.js';
|
|||||||
import * as pipelineRepo from '../repositories/pipeline.js';
|
import * as pipelineRepo from '../repositories/pipeline.js';
|
||||||
import { runPipeline, processJob, getPipelineStatus } from '../pipeline/index.js';
|
import { runPipeline, processJob, getPipelineStatus } from '../pipeline/index.js';
|
||||||
import { createNotionEntry } from '../services/notion.js';
|
import { createNotionEntry } from '../services/notion.js';
|
||||||
|
import { clearDatabase } from '../db/clear.js';
|
||||||
import type { JobStatus, ApiResponse, JobsListResponse, PipelineStatusResponse } from '../../shared/types.js';
|
import type { JobStatus, ApiResponse, JobsListResponse, PipelineStatusResponse } from '../../shared/types.js';
|
||||||
|
|
||||||
export const apiRouter = Router();
|
export const apiRouter = Router();
|
||||||
@ -270,3 +271,28 @@ apiRouter.post('/webhook/trigger', async (req: Request, res: Response) => {
|
|||||||
res.status(500).json({ success: false, error: message });
|
res.status(500).json({ success: false, error: message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Database Management
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE /api/database - Clear all data from the database
|
||||||
|
*/
|
||||||
|
apiRouter.delete('/database', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const result = clearDatabase();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
message: 'Database cleared',
|
||||||
|
jobsDeleted: result.jobsDeleted,
|
||||||
|
runsDeleted: result.runsDeleted,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
||||||
|
res.status(500).json({ success: false, error: message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
56
orchestrator/src/server/db/clear.ts
Normal file
56
orchestrator/src/server/db/clear.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Database utility scripts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Database from 'better-sqlite3';
|
||||||
|
import { join, dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const DB_PATH = join(__dirname, '../../../data/jobs.db');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all data from the database (keeps the schema intact).
|
||||||
|
*/
|
||||||
|
export function clearDatabase(): { jobsDeleted: number; runsDeleted: number } {
|
||||||
|
const sqlite = new Database(DB_PATH);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const jobsResult = sqlite.prepare('DELETE FROM jobs').run();
|
||||||
|
const runsResult = sqlite.prepare('DELETE FROM pipeline_runs').run();
|
||||||
|
|
||||||
|
console.log(`🗑️ Cleared database: ${jobsResult.changes} jobs, ${runsResult.changes} pipeline runs`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
jobsDeleted: jobsResult.changes,
|
||||||
|
runsDeleted: runsResult.changes,
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
sqlite.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete database file completely (will recreate on next run).
|
||||||
|
*/
|
||||||
|
export function dropDatabase(): void {
|
||||||
|
const { unlinkSync, existsSync } = require('fs');
|
||||||
|
|
||||||
|
if (existsSync(DB_PATH)) {
|
||||||
|
unlinkSync(DB_PATH);
|
||||||
|
console.log('🗑️ Database file deleted');
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ No database file to delete');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI execution
|
||||||
|
if (process.argv[1]?.includes('clear.ts')) {
|
||||||
|
const arg = process.argv[2];
|
||||||
|
|
||||||
|
if (arg === '--drop') {
|
||||||
|
dropDatabase();
|
||||||
|
} else {
|
||||||
|
clearDatabase();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user