├── backend ├── app │ ├── __init__.py │ ├── core │ │ └── limiter.py │ ├── utils │ │ └── format_message.py │ ├── main.py │ ├── services │ │ ├── claude_service.py │ │ ├── o3_mini_openrouter_service.py │ │ ├── o3_mini_openai_service.py │ │ ├── o1_mini_openai_service.py │ │ └── github_service.py │ ├── routers │ │ ├── modify.py │ │ └── generate.py │ └── prompts.py ├── deploy.sh ├── nginx │ ├── setup_nginx.sh │ └── api.conf ├── entrypoint.sh ├── Dockerfile └── requirements.txt ├── postcss.config.js ├── docs └── readme_img.png ├── public └── favicon.ico ├── prettier.config.js ├── src ├── lib │ ├── utils.ts │ ├── exampleRepos.ts │ └── fetch-backend.ts ├── components │ ├── loading-animation.tsx │ ├── api-key-button.tsx │ ├── footer.tsx │ ├── ui │ │ ├── textarea.tsx │ │ ├── input.tsx │ │ ├── progress.tsx │ │ ├── switch.tsx │ │ ├── tooltip.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ └── dialog.tsx │ ├── export-dropdown.tsx │ ├── action-button.tsx │ ├── copy-button.tsx │ ├── customization-dropdown.tsx │ ├── hero.tsx │ ├── mermaid-diagram.tsx │ ├── header.tsx │ ├── api-key-dialog.tsx │ ├── private-repos-dialog.tsx │ ├── loading.tsx │ └── main-card.tsx ├── app │ ├── _actions │ │ ├── repo.ts │ │ ├── github.ts │ │ └── cache.ts │ ├── page.tsx │ ├── providers.tsx │ ├── layout.tsx │ └── [username] │ │ └── [repo] │ │ └── page.tsx ├── server │ └── db │ │ ├── index.ts │ │ └── schema.ts ├── env.js ├── styles │ └── globals.css └── hooks │ └── useDiagram.ts ├── drizzle.config.ts ├── docker-compose.yml ├── stop-services.sh ├── .env.example ├── drizzle ├── meta │ ├── _journal.json │ ├── 0000_snapshot.json │ └── 0001_snapshot.json ├── 0000_jittery_robin_chapel.sql └── 0001_lush_mercury.sql ├── components.json ├── update_ec2.sh ├── .gitignore ├── next.config.js ├── tsconfig.json ├── start-services.sh ├── LICENSE ├── .eslintrc.cjs ├── start-database.sh ├── package.json ├── tailwind.config.ts └── README.md /backend/app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /docs/readme_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VGabriel45/SolidityVisualizer/HEAD/docs/readme_img.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VGabriel45/SolidityVisualizer/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /backend/app/core/limiter.py: -------------------------------------------------------------------------------- 1 | from slowapi import Limiter 2 | from slowapi.util import get_remote_address 3 | 4 | limiter = Limiter(key_func=get_remote_address) 5 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */ 2 | export default { 3 | plugins: ["prettier-plugin-tailwindcss"], 4 | }; 5 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/exampleRepos.ts: -------------------------------------------------------------------------------- 1 | export const exampleRepos = { 2 | UniswapV4Periphery: "Uniswap/v4-periphery", 3 | UniswapV3Core: "Uniswap/v3-core", 4 | TornadoCashRebuilt: "nkrishang/tornado-cash-rebuilt", 5 | }; 6 | -------------------------------------------------------------------------------- /src/components/loading-animation.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { trio } from "ldrs"; 4 | 5 | trio.register(); 6 | 7 | const LoadingAnimation = () => { 8 | return ; 9 | }; 10 | 11 | export default LoadingAnimation; 12 | -------------------------------------------------------------------------------- /drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { type Config } from "drizzle-kit"; 2 | 3 | import { env } from "~/env"; 4 | 5 | export default { 6 | schema: "./src/server/db/schema.ts", 7 | dialect: "postgresql", 8 | dbCredentials: { 9 | url: env.POSTGRES_URL, 10 | }, 11 | tablesFilter: ["solidityVisualizer_*"], 12 | } satisfies Config; 13 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | api: 3 | build: 4 | context: ./backend 5 | dockerfile: Dockerfile 6 | ports: 7 | - "8000:8000" 8 | volumes: 9 | - ./backend:/app 10 | env_file: 11 | - .env 12 | environment: 13 | - ENVIRONMENT=${ENVIRONMENT:-development} # Default to development if not set 14 | restart: unless-stopped 15 | -------------------------------------------------------------------------------- /stop-services.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "🛑 Stopping Solidity Visualizer services..." 4 | 5 | # Stop server containers 6 | echo "🖥️ Stopping server..." 7 | docker compose down 8 | 9 | # Stop database container 10 | echo "📦 Stopping database..." 11 | docker stop postgres-db 2>/dev/null || true 12 | docker rm postgres-db 2>/dev/null || true 13 | 14 | echo "✅ All services stopped successfully!" -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | POSTGRES_URL="postgresql://postgres:password@localhost:5432/solidityVisualizer" 2 | NEXT_PUBLIC_API_DEV_URL=http://localhost:8000 3 | NEXT_PUBLIC_API_PROD_URL= 4 | 5 | OPENAI_API_KEY= 6 | 7 | # OPTIONAL: providing your own GitHub PAT increases rate limits from 60/hr to 5000/hr to the GitHub API 8 | GITHUB_PAT= 9 | 10 | # old implementation 11 | # OPENROUTER_API_KEY= 12 | # ANTHROPIC_API_KEY= -------------------------------------------------------------------------------- /drizzle/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "postgresql", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "7", 8 | "when": 1743445628031, 9 | "tag": "0000_jittery_robin_chapel", 10 | "breakpoints": true 11 | }, 12 | { 13 | "idx": 1, 14 | "version": "7", 15 | "when": 1743446339371, 16 | "tag": "0001_lush_mercury", 17 | "breakpoints": true 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/app/_actions/repo.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { db } from "~/server/db"; 4 | import { eq, and } from "drizzle-orm"; 5 | import { diagramCache } from "~/server/db/schema"; 6 | 7 | export async function getLastGeneratedDate(username: string, repo: string) { 8 | const result = await db 9 | .select() 10 | .from(diagramCache) 11 | .where( 12 | and(eq(diagramCache.username, username), eq(diagramCache.repo, repo)), 13 | ); 14 | 15 | return result[0]?.updatedAt; 16 | } 17 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/styles/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "~/components", 15 | "utils": "~/lib/utils", 16 | "ui": "~/components/ui", 17 | "lib": "~/lib", 18 | "hooks": "~/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /drizzle/0000_jittery_robin_chapel.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS "solidityAnalyzer_diagram_cache" ( 2 | "username" varchar(256) NOT NULL, 3 | "repo" varchar(256) NOT NULL, 4 | "diagram" varchar(10000) NOT NULL, 5 | "explanation" varchar(10000) DEFAULT 'No explanation provided' NOT NULL, 6 | "created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, 7 | "updated_at" timestamp with time zone, 8 | "used_own_key" boolean DEFAULT false, 9 | CONSTRAINT "solidityAnalyzer_diagram_cache_username_repo_pk" PRIMARY KEY("username","repo") 10 | ); 11 | -------------------------------------------------------------------------------- /update_ec2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Stopping containers on EC2..." 4 | ssh solidity-visualizer 'cd /home/ec2-user/solidity-visualizer && sudo docker-compose down' 5 | 6 | echo "Copying backend files to EC2..." 7 | scp -r backend/* solidity-visualizer:/home/ec2-user/solidity-visualizer/backend/ 8 | 9 | echo "Rebuilding and starting containers on EC2..." 10 | ssh solidity-visualizer 'cd /home/ec2-user/solidity-visualizer && sudo docker-compose up --build -d' 11 | 12 | echo "Showing logs..." 13 | ssh solidity-visualizer 'cd /home/ec2-user/solidity-visualizer && sudo docker-compose logs -f api' -------------------------------------------------------------------------------- /backend/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit on any error 4 | set -e 5 | 6 | # Navigate to project directory (one level up from backend) 7 | cd .. 8 | 9 | # Pull latest changes 10 | git pull origin main 11 | 12 | # Build and restart containers with production environment 13 | docker-compose down 14 | ENVIRONMENT=production docker-compose up --build -d 15 | 16 | # Remove unused images 17 | docker image prune -f 18 | 19 | # Show logs only if --logs flag is passed 20 | if [ "$1" == "--logs" ]; then 21 | docker-compose logs -f 22 | else 23 | echo "Deployment complete! Run 'docker-compose logs -f' to view logs" 24 | fi -------------------------------------------------------------------------------- /drizzle/0001_lush_mercury.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS "solidityVisualizer_diagram_cache" ( 2 | "username" varchar(256) NOT NULL, 3 | "repo" varchar(256) NOT NULL, 4 | "diagram" varchar(10000) NOT NULL, 5 | "explanation" varchar(10000) DEFAULT 'No explanation provided' NOT NULL, 6 | "created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, 7 | "updated_at" timestamp with time zone, 8 | "used_own_key" boolean DEFAULT false, 9 | CONSTRAINT "solidityVisualizer_diagram_cache_username_repo_pk" PRIMARY KEY("username","repo") 10 | ); 11 | --> statement-breakpoint 12 | DROP TABLE "solidityAnalyzer_diagram_cache"; -------------------------------------------------------------------------------- /src/components/api-key-button.tsx: -------------------------------------------------------------------------------- 1 | import { Key } from "lucide-react"; 2 | import { Button } from "./ui/button"; 3 | 4 | interface ApiKeyButtonProps { 5 | onClick: () => void; 6 | } 7 | 8 | export function ApiKeyButton({ onClick }: ApiKeyButtonProps) { 9 | return ( 10 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /backend/nginx/setup_nginx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit on any error 4 | set -e 5 | 6 | # Check if running as root 7 | if [ "$EUID" -ne 0 ]; then 8 | echo "Please run as root or with sudo" 9 | exit 1 10 | fi 11 | 12 | # Copy Nginx configuration 13 | echo "Copying Nginx configuration..." 14 | cp "$(dirname "$0")/api.conf" /etc/nginx/sites-available/api 15 | ln -sf /etc/nginx/sites-available/api /etc/nginx/sites-enabled/ 16 | 17 | # Test Nginx configuration 18 | echo "Testing Nginx configuration..." 19 | nginx -t 20 | 21 | # Reload Nginx 22 | echo "Reloading Nginx..." 23 | systemctl reload nginx 24 | 25 | echo "Nginx configuration updated successfully!" -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import MainCard from "~/components/main-card"; 2 | import Hero from "~/components/hero"; 3 | 4 | export default function HomePage() { 5 | return ( 6 |
7 |
8 | 9 |
10 |

11 | Turn any Web3 Protocol into a complex architecture diagram. 12 |

13 |
14 |
15 | 16 |
17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/components/footer.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "next/link"; 3 | 4 | export function Footer() { 5 | return ( 6 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /backend/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Current ENVIRONMENT: $ENVIRONMENT" 4 | 5 | if [ "$ENVIRONMENT" = "development" ]; then 6 | echo "Starting in development mode with hot reload..." 7 | exec uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload 8 | elif [ "$ENVIRONMENT" = "production" ]; then 9 | echo "Starting in production mode with multiple workers..." 10 | exec uvicorn app.main:app \ 11 | --host 0.0.0.0 \ 12 | --port 8000 \ 13 | --timeout-keep-alive 300 \ 14 | --workers 2 \ 15 | --loop uvloop \ 16 | --http httptools 17 | else 18 | echo "ENVIRONMENT must be set to either 'development' or 'production'" 19 | exit 1 20 | fi -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "~/lib/utils"; 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.ComponentProps<"textarea"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |