Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions src/components/RecentScansSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { ExternalLink, Clock, ArrowUpRight, ChevronRight } from 'lucide-react';
import { formatDistanceToNow } from "date-fns";
import { getRecentScans } from '../services/api';

type Scan = {
_id: string;
Expand All @@ -26,17 +27,8 @@ const RecentScansSection = () => {
useEffect(() => {
const fetchRecentScans = async () => {
try {
// Import the API function at the top of file
// import { getRecentScans } from '../services/api';
const data = await fetch('http://localhost:5000/api/recent-scans')
.then(res => {
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
return res.json();
});



setRecentScans(data);
const data = await getRecentScans(5);
setRecentScans(data as unknown as Scan[]);
} catch (err) {
console.error("Error fetching recent scans:", err);
// You can add toast notification here
Expand Down
39 changes: 17 additions & 22 deletions src/hooks/useScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import { useState } from 'react';
import { scanWebsite, getReport, getRecentScans } from '../services/api';
import type { ScanResult } from '../types';

// Define a type that includes possible scanId/id fields
type ScanIdentifier = { scanId?: string; id?: string };

export const useScanner = () => {
const [isScanning, setIsScanning] = useState(false);
const [error, setError] = useState<string | null>(null);
Expand All @@ -21,46 +18,44 @@ export const useScanner = () => {
setResult(scanResult);

// Use id or scanId as identifier
const identifier =
(scanResult as ScanIdentifier).scanId ||
(scanResult as ScanIdentifier).id;
let currentResult = { ...scanResult, identifier } as ScanResult & {
status?: string;
identifier?: string;
};
const identifier = scanResult.scanId || scanResult.id;
let currentResult: ScanResult = { ...scanResult, identifier };

const pollInterval = 3000; // 3 seconds
const maxAttempts = 100; // ~5 minutes at 3s interval
let attempts = 0;

if (!currentResult || !currentResult.identifier) {
throw new Error('Scan did not return a valid scan identifier.');
}
if (!currentResult.status) {
// If status is missing, try to fetch the report once
const fetched = (await getReport(currentResult.identifier)) as ScanResult & {
status?: string;
identifier?: string;
};
const fetched = await getReport(currentResult.identifier);
setResult(fetched);
return fetched;
}

// Poll until scan is completed
// Poll until scan is completed, or until we time out
while (currentResult.status === 'in_progress') {
if (attempts++ >= maxAttempts) {
throw new Error('Scan timed out. Please try again.');
}

await new Promise((resolve) => setTimeout(resolve, pollInterval));

if (!currentResult.identifier) {
throw new Error('Scan identifier is missing during polling.');
}
const updatedReport = (await getReport(currentResult.identifier)) as ScanResult & {
status?: string;
identifier?: string;
};

const updatedReport = await getReport(currentResult.identifier);
console.log('Polled report:', updatedReport);
setResult(updatedReport);

currentResult = {
...updatedReport,
identifier:
(updatedReport as ScanIdentifier).scanId ||
(updatedReport as ScanIdentifier).id,
identifier: updatedReport.scanId || updatedReport.id,
};

if (!currentResult.status) break;
}

Expand Down
40 changes: 8 additions & 32 deletions src/pages/HistoryPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { useState, useEffect } from 'react';
import { captureEvent } from '../utils/posthog/helpers';
import { EVENTS } from '../utils/posthog/events';
import { useNavigate } from 'react-router-dom';
import { motion } from 'framer-motion';
import {
Expand All @@ -13,6 +11,7 @@ import {
RefreshCw,
AlertCircle
} from 'lucide-react';
import { getReports, deleteScans } from '../services/api';

// Define Scan type to match your backend response
type Scan = {
Expand All @@ -37,22 +36,17 @@ const HistoryPage = () => {
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
const [selectedScans, setSelectedScans] = useState<string[]>([]);
const [successMessage, setSuccessMessage] = useState<string | null>(null);

// Fetch scan history from backend
const fetchScanHistory = async () => {
try {
setLoading(true);
setError(null);

// Use the correct endpoint that exists in your backend
const response = await fetch('http://localhost:5000/api/reports?limit=50');
if (!response.ok) {
throw new Error(`Failed to fetch scan history: ${response.statusText}`);
}

const data = await response.json();
const data = await getReports(50, 0);

// Transform the data to match our Scan type
const transformedData: Scan[] = data.map((scan: {
const rawScans = data as unknown as Array<{
id?: string;
_id?: string;
url: string;
Expand All @@ -68,7 +62,8 @@ const HistoryPage = () => {
issues?: unknown[];
};
status?: 'completed' | 'failed' | 'pending';
}) => ({
}>;
const transformedData: Scan[] = rawScans.map((scan) => ({
id: scan.id || scan._id,
url: scan.url,
// Parse date as-is (assume backend returns UTC ISO string)
Expand All @@ -93,18 +88,7 @@ const HistoryPage = () => {
// Delete scans from backend
const deleteScansFromBackend = async (scanIds: string[]) => {
try {
const response = await fetch('http://localhost:5000/api/scans/delete', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ ids: scanIds }),
});

if (!response.ok) {
throw new Error('Failed to delete scans from server');
}

await deleteScans(scanIds);
return true;
} catch (err) {
console.error('Error deleting scans:', err);
Expand All @@ -127,14 +111,7 @@ const HistoryPage = () => {

// Handle search
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setSearchTerm(value);
if (value.length > 2) {
captureEvent(EVENTS.HISTORY_SEARCHED, {
search_term: value,
result_count: scanHistory.filter(s => s.url.toLowerCase().includes(value.toLowerCase())).length,
});
}
setSearchTerm(e.target.value);
};

// Toggle sort order
Expand Down Expand Up @@ -170,7 +147,6 @@ const HistoryPage = () => {
const success = await deleteScansFromBackend(selectedScans);

if (success) {
captureEvent(EVENTS.HISTORY_SCANS_DELETED, { deleted_count: selectedScans.length });
// Remove from local state
setScanHistory(prev => prev.filter(scan => !selectedScans.includes(scan.id)));
setSelectedScans([]);
Expand Down
15 changes: 15 additions & 0 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@ export const getScanReport = async (id: string): Promise<ScanResult> => {
}
};

export const deleteScans = async (ids: string[]): Promise<{ deleted: number }> => {
try {
const response = await api.delete('/scans/delete', { data: { ids } });
return response.data;
} catch (error: unknown) {
console.error('Delete Scans Error:', error);
const apiError = error as ApiError;
const errorMessage =
apiError.response?.data?.error ||
apiError.response?.data?.message ||
'Failed to delete scans. Please try again later.';
throw new Error(errorMessage);
}
};

// Health check function
export const checkHealth = async (): Promise<boolean> => {
try {
Expand Down
Loading