Skip to content

Commit

Permalink
97%
Browse files Browse the repository at this point in the history
  • Loading branch information
Levironexe committed Nov 4, 2024
1 parent 54b607c commit f9f3bcc
Show file tree
Hide file tree
Showing 23 changed files with 448 additions and 108 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ PORT=5000
NEXT_PUBLIC_API_URL=http://localhost:5000

# File Size Limit (in bytes)
MAX_FILE_SIZE=10485760
MAX_FILE_SIZE=200485760


#solidityscan key
Expand Down
41 changes: 40 additions & 1 deletion app/api/backend.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ async function setupApplication() {
markdown_content: markdownContent,
}
});

} catch (error) {
console.error('Analysis error:', error);
if (filePath) {
Expand All @@ -539,6 +539,45 @@ async function setupApplication() {
}
});

app.get('/contract/:id', async (req, res) => {
try {
const { id } = req.params;

// Check if ID is malformed (contains Object or %20)
if (id.includes('Object') || id.includes('%20')) {
const cleanId = id.replace(/Object|%20/g, '');
return res.redirect(301, `/contract/${cleanId}`);
}

// Get data from Supabase
const { data, error } = await supabase
.from('slither_metrics')
.select('*')
.eq('id', id)
.single();

if (error) throw error;

if (!data) {
return res.status(404).json({
status: 'error',
message: 'Contract not found'
});
}

res.json({
status: 'success',
data
});

} catch (error) {
console.error('Error:', error);
res.status(500).json({
status: 'error',
message: error.message
});
}
});

// Start server
const PORT = process.env.PORT || 5000;
Expand Down
Binary file removed app/api/result.md
Binary file not shown.
23 changes: 20 additions & 3 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -318,14 +318,31 @@ background: linear-gradient(45deg,#9333ea, #e70606);
line-height: 1.6;
}

.markdown-content h2, .markdown-content h3, .markdown-content li, .markdown-content p {
.markdown-content h2 {
color: #000000;
font-size: 20px;
font-weight: bold;
padding-top: 20px;
text-transform: capitalize;
}
.markdown-content ul li input[type="checkbox"] {
appearance: none;
display: block;
}

.markdown-content h3, .markdown-content li {
color: #494949;
display: block;

}

.markdown-content a {
color: rgb(231, 54, 6);
text-decoration: underline;
pointer-events: none;
}

/* END: search manufacturer styles */
.markdown-content p {
font-weight: bold;
color: #494949;
}
/*end*/
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function RootLayout({
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-black`}
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-black h-auto`}
>
<SessionProvider>
<Navbar/>
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 12 additions & 0 deletions app/projects/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import {AllProjects} from '@/components/index'

const AllProject = () => {
return (
<main>
<AllProjects/>
</main>
)
}

export default AllProject
6 changes: 3 additions & 3 deletions components/button/upload-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {motion} from "framer-motion"; // Animation library

interface UploadFormProps {
style?:string
title:string
title: React.ReactNode | string
}

const UploadForm = ({style, title}: UploadFormProps) => {
Expand Down Expand Up @@ -115,7 +115,7 @@ const UploadForm = ({style, title}: UploadFormProps) => {

localStorage.setItem('metricsId', metricsId);
setMessage('Upload successful! The scan has started.');
startScanning(analyzeResponse.data.data);
startScanning(metricsId);
// Clear form
setProjectName('');
setContractFile(null);
Expand Down Expand Up @@ -147,7 +147,7 @@ const UploadForm = ({style, title}: UploadFormProps) => {
<CustomButton
handleClick={handleUploadButton}
style={`${style}`}
title={`${title}`}
title={title}
/>

{openUpload && (
Expand Down
5 changes: 4 additions & 1 deletion components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import TokenBasicInfo from './section/token-basic-info';
import UploadForm from './button/upload-form';
import { Overview } from './section/overview';
import VulnerabilityList from './section/vulnerability-list'
import NoContractFound from './section/no-contract-found';
import AllProjects from './section/all-projects';
export {
Hero,
Navbar,
Expand All @@ -46,5 +48,6 @@ export {
UploadForm,
VulnerabilityList,
Overview,

NoContractFound,
AllProjects
}
227 changes: 227 additions & 0 deletions components/section/all-projects.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
'use client'
import React, { useState, useEffect } from 'react';
import { createClient } from '@supabase/supabase-js';
import Link from 'next/link';
import { Search, FilePlus, Target} from 'lucide-react';
import {UploadForm} from '@/components/index'

interface AnalysisMetrics {
id: string;
project_name: string;
created_at: string;
total_contracts: number;
source_lines: number;
assembly_lines: number;
scan_duration: number;
optimization_issues: number;
informational_issues: number;
low_issues: number;
medium_issues: number;
high_issues: number;
ercs: string;
markdown_content: string;
security_score?: number;
}

const calculateSafetyScore = (metrics: AnalysisMetrics): number => {
const baseScore = 100;
const deductions = {
high: 5,
medium: 2,
low: 1,
informational: 0.2,
optimization: 0.1
};

const totalIssues = metrics.high_issues + metrics.medium_issues +
metrics.low_issues + metrics.informational_issues +
metrics.optimization_issues;

const totalDeduction =
(metrics.high_issues * deductions.high) +
(metrics.medium_issues * deductions.medium) +
(metrics.low_issues * deductions.low) +
(metrics.informational_issues * deductions.informational) +
(metrics.optimization_issues * deductions.optimization);

return Number((Math.max(0, Math.min(100, baseScore - totalDeduction - (100*2.5*totalIssues/metrics.source_lines)))).toFixed(2));
};

const AllProjects = () => {
const [loading, setLoading] = useState(true);
const [projects, setProjects] = useState<AnalysisMetrics[]>([]);
const [error, setError] = useState<string | null>(null);
const [searchTerm, setSearchTerm] = useState('');
const [filteredProjects, setFilteredProjects] = useState<AnalysisMetrics[]>([]);

const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
auth: { persistSession: false },
global: {
headers: {
'apikey': process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
'Authorization': `Bearer ${process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!}`
}
}
}
);

useEffect(() => {
const fetchProjects = async () => {
try {
setLoading(true);
const sessionId = localStorage.getItem('sessionId');

if (!sessionId) {
setError('No session ID found. Please log in again.');
return;
}

const { data, error: fetchError } = await supabase
.from('slither_metrics')
.select('*')
.order('created_at', { ascending: false })
.eq('session_id', sessionId);

if (fetchError) throw fetchError;

if (data) {
const projectsWithScores = data.map(project => ({
...project,
security_score: calculateSafetyScore(project)
}));
setProjects(projectsWithScores);
setFilteredProjects(projectsWithScores);
}
} catch (error) {
console.error('Error fetching projects:', error);
setError('Failed to load projects. Please try again later.');
} finally {
setLoading(false);
}
};

fetchProjects();
}, []);

useEffect(() => {
const filtered = projects.filter(project =>
project.project_name.toLowerCase().includes(searchTerm.toLowerCase())
);
setFilteredProjects(filtered);
}, [searchTerm, projects]);

if (loading) {
return (
<div className="min-h-screen bg-white__bg flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-4 border-primary-red border-t-transparent"></div>
</div>
);
}

if (error) {
return (
<div className="min-h-screen bg-white__bg flex items-center justify-center">
<div className="text-primary-red font-bold text-xl text-center">
{error}
</div>
</div>
);
}


return (
<div className="min-h-screen bg-gray-50 py-8 px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto py-4 sm:py-6 lg:py-8 px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center mb-12">
<h1 className="items-center flex gap-2 text-2xl font-bold bg-gradient-to-r from-primary-red to-pink-600 text-transparent bg-clip-text">PROJECTS</h1>

{/* Search and Filter Section */}
<div className="flex items-center gap-4">
<div className="relative">
<input
type="text"
placeholder="Search by Project name"
className="pl-10 pr-4 py-2 border border-gray-300 rounded-xl w-[300px]"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<Search className="absolute left-3 top-2.5 h-5 w-5 text-gray-400" />
</div>

<UploadForm style='py-2 px-3 justify-center text-white/80 rounded-xl text-base sm:text-[14px] xl:text-[18px] font-normal w-full sm:w-auto hover:bg-white hover:text-white
bg-gradient-to-r from-primary-red to-pink-600
hover:bg-gradient-to-r hover:from-primary-red hover:to-primary-red transition-all duration-300'
title={
<>
<FilePlus size={20} />
<span>New project</span>
</>}
/>
</div>
</div>

{/* Projects Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredProjects.map((project) => (
<Link href={`/project/${project.id}`} key={project.id}>
<div className="bg-white rounded-xl shadow-sm p-6 hover:shadow-md transition-shadow">
<div className="flex justify-between items-start mb-4">
<div>
<h2 className="text-lg font-semibold text-gray-900 mb-1">{project.project_name}</h2>
<p className="text-sm text-gray-500">
Last scanned {new Date(project.created_at).toLocaleDateString()}
</p>
</div>
<div>
<p className={`text-right text-[20px] font-bold `}>{project.security_score}</p>
<p className='text-sm text-gray-500'>Security score</p>
</div>
</div>

{/* Metrics */}
<div className="grid grid-cols-5 gap-2 text-center text-sm">
<div>
<div className="font-medium">{project.high_issues}</div>
<div className="text-pri">High</div>
<div className='border-b-[3px] w-1/2 mx-auto mt-2 border-red-600'></div>
</div>
<div>
<div className="font-medium">{project.medium_issues}</div>
<div className="text-gray-500">Med</div>
<div className='border-b-[3px] w-1/2 mx-auto mt-2 border-yellow-400'></div>
</div>
<div>
<div className="font-medium">{project.low_issues}</div>
<div className="text-gray-500">Low</div>
<div className='border-b-[3px] w-1/2 mx-auto mt-2 border-green-500'></div>
</div>
<div>
<div className="font-medium">{project.informational_issues}</div>
<div className="text-gray-500">Info</div>
<div className='border-b-[3px] w-1/2 mx-auto mt-2 border-gray-500'></div>
</div>
<div>
<div className="font-medium">{project.optimization_issues}</div>
<div className="text-gray-500">Opti</div>
<div className='border-b-[3px] w-1/2 mx-auto mt-2 border-blue-500'></div>
</div>
</div>
</div>
</Link>
))}
</div>

{filteredProjects.length === 0 && !loading && (
<div className="text-center py-12">
<p className="text-gray-500 text-lg">No projects found</p>
</div>
)}
</div>
</div>
);
};

export default AllProjects;
Loading

0 comments on commit f9f3bcc

Please sign in to comment.