clear db button

This commit is contained in:
DaKheera47 2025-12-11 22:47:45 +00:00
parent f9352ad5b0
commit a6a4cb9f89
7 changed files with 141 additions and 2 deletions

View File

@ -13,6 +13,8 @@
"build:client": "vite build",
"start": "node dist/server/index.js",
"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"
},
"dependencies": {
@ -40,4 +42,4 @@
"typescript": "^5.7.2",
"vite": "^6.0.3"
}
}
}

View File

@ -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 (
<>
<Header
onRunPipeline={handleRunPipeline}
onRefresh={loadJobs}
onClearDatabase={handleClearDatabase}
isPipelineRunning={isPipelineRunning}
isLoading={isLoading}
/>

View File

@ -89,3 +89,18 @@ export async function runPipeline(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',
});
}

View File

@ -3,11 +3,12 @@
*/
import React from 'react';
import { RocketIcon, PlayIcon, RefreshIcon } from './Icons';
import { RocketIcon, PlayIcon, RefreshIcon, TrashIcon } from './Icons';
interface HeaderProps {
onRunPipeline: () => void;
onRefresh: () => void;
onClearDatabase: () => void;
isPipelineRunning: boolean;
isLoading: boolean;
}
@ -15,9 +16,16 @@ interface HeaderProps {
export const Header: React.FC<HeaderProps> = ({
onRunPipeline,
onRefresh,
onClearDatabase,
isPipelineRunning,
isLoading,
}) => {
const handleClearDatabase = () => {
if (window.confirm('Are you sure you want to clear all jobs from the database? This cannot be undone.')) {
onClearDatabase();
}
};
return (
<header className="header">
<div className="container">
@ -30,6 +38,16 @@ export const Header: React.FC<HeaderProps> = ({
</div>
<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
className="btn btn-ghost"
onClick={onRefresh}

View File

@ -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"/>
</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>
);

View File

@ -8,6 +8,7 @@ import * as jobsRepo from '../repositories/jobs.js';
import * as pipelineRepo from '../repositories/pipeline.js';
import { runPipeline, processJob, getPipelineStatus } from '../pipeline/index.js';
import { createNotionEntry } from '../services/notion.js';
import { clearDatabase } from '../db/clear.js';
import type { JobStatus, ApiResponse, JobsListResponse, PipelineStatusResponse } from '../../shared/types.js';
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 });
}
});
// ============================================================================
// 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 });
}
});

View 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();
}
}