Skip to content

Cloud-scan/cloudscan-ui

Repository files navigation

cloudscan-ui

Full-stack UI service for CloudScan - Spring Boot backend + React frontend with real-time security scanning dashboard


🎨 Overview

The cloudscan-ui is a full-stack web application that serves as the interface for CloudScan platform. It combines:

  • Backend (Spring Boot): Lightweight API server for authentication, session management, and proxying requests
  • Frontend (React): Beautiful, responsive UI for security scanning

Features:

  • πŸ” User authentication (login/signup) with session management
  • πŸ“Š Real-time scan progress with live logs
  • πŸ” Security findings dashboard with filters
  • πŸ“ˆ Analytics and trends
  • βš™οΈ Project and organization management
  • πŸ“± Responsive design (desktop, tablet, mobile)
  • πŸŒ“ Dark/light theme support
  • πŸ›‘οΈ CSRF protection and security headers
  • πŸ”„ API Gateway proxy to backend services

πŸ—οΈ Tech Stack

Backend

  • Framework: Spring Boot 3.2+
  • Language: Java 17+
  • Security: Spring Security (JWT/Session)
  • Database: PostgreSQL (user sessions, preferences)
  • API Client: WebClient (for backend service calls)
  • Build Tool: Maven/Gradle

Frontend

  • Framework: React 18 + TypeScript
  • Build Tool: Vite
  • Styling: TailwindCSS + HeadlessUI
  • State Management: Zustand
  • Data Fetching: TanStack Query (React Query)
  • Routing: React Router v6
  • Forms: React Hook Form + Zod
  • Charts: Recharts
  • WebSocket: native WebSocket API
  • Icons: Heroicons

πŸ“ Project Structure

cloudscan-ui/
β”œβ”€β”€ backend/                          # Spring Boot backend
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   └── main/
β”‚   β”‚       β”œβ”€β”€ java/com/cloudscan/ui/
β”‚   β”‚       β”‚   β”œβ”€β”€ CloudscanUiApplication.java
β”‚   β”‚       β”‚   β”œβ”€β”€ config/
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ SecurityConfig.java      # Spring Security config
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ WebClientConfig.java     # WebClient for backend calls
β”‚   β”‚       β”‚   β”‚   └── CorsConfig.java          # CORS configuration
β”‚   β”‚       β”‚   β”œβ”€β”€ controller/
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ AuthController.java      # Login/logout endpoints
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ ProxyController.java     # Proxy to backend services
β”‚   β”‚       β”‚   β”‚   └── UserController.java      # User management
β”‚   β”‚       β”‚   β”œβ”€β”€ service/
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ AuthService.java         # Authentication logic
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ SessionService.java      # Session management
β”‚   β”‚       β”‚   β”‚   └── BackendProxyService.java # Backend service proxy
β”‚   β”‚       β”‚   β”œβ”€β”€ model/
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ User.java
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ Session.java
β”‚   β”‚       β”‚   β”‚   └── dto/                     # DTOs for API
β”‚   β”‚       β”‚   β”œβ”€β”€ repository/
β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ UserRepository.java
β”‚   β”‚       β”‚   β”‚   └── SessionRepository.java
β”‚   β”‚       β”‚   └── security/
β”‚   β”‚       β”‚       β”œβ”€β”€ JwtTokenProvider.java
β”‚   β”‚       β”‚       └── UserDetailsServiceImpl.java
β”‚   β”‚       └── resources/
β”‚   β”‚           β”œβ”€β”€ application.yml              # Spring Boot config
β”‚   β”‚           β”œβ”€β”€ application-dev.yml
β”‚   β”‚           └── application-prod.yml
β”‚   β”œβ”€β”€ pom.xml / build.gradle                   # Maven/Gradle config
β”‚   └── README.md
β”‚
β”œβ”€β”€ frontend/                         # React frontend
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ components/           # Reusable UI components
β”‚   β”‚   β”‚   β”œβ”€β”€ common/          # Buttons, inputs, modals, etc.
β”‚   β”‚   β”‚   β”œβ”€β”€ scan/            # Scan-specific components
β”‚   β”‚   β”‚   β”œβ”€β”€ findings/        # Finding display components
β”‚   β”‚   β”‚   └── layout/          # Layout components (Header, Sidebar)
β”‚   β”‚   β”œβ”€β”€ pages/               # Page components
β”‚   β”‚   β”‚   β”œβ”€β”€ Login.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ Signup.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ Dashboard.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ Projects.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ ScanNew.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ ScanDetails.tsx
β”‚   β”‚   β”‚   └── FindingsPage.tsx
β”‚   β”‚   β”œβ”€β”€ hooks/               # Custom React hooks
β”‚   β”‚   β”‚   β”œβ”€β”€ useAuth.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ useScans.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ useWebSocket.ts
β”‚   β”‚   β”‚   └── useFindings.ts
β”‚   β”‚   β”œβ”€β”€ services/            # API clients
β”‚   β”‚   β”‚   β”œβ”€β”€ api.ts           # Base API client (calls Spring Boot backend)
β”‚   β”‚   β”‚   β”œβ”€β”€ scans.ts         # Scan API
β”‚   β”‚   β”‚   β”œβ”€β”€ auth.ts          # Authentication API
β”‚   β”‚   β”‚   └── websocket.ts     # WebSocket client
β”‚   β”‚   β”œβ”€β”€ utils/               # Utility functions
β”‚   β”‚   β”‚   β”œβ”€β”€ format.ts        # Date/number formatting
β”‚   β”‚   β”‚   └── constants.ts     # App constants
β”‚   β”‚   β”œβ”€β”€ types/               # TypeScript types
β”‚   β”‚   β”‚   β”œβ”€β”€ scan.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ finding.ts
β”‚   β”‚   β”‚   └── user.ts
β”‚   β”‚   β”œβ”€β”€ stores/              # Zustand stores
β”‚   β”‚   β”‚   β”œβ”€β”€ authStore.ts
β”‚   β”‚   β”‚   └── uiStore.ts
β”‚   β”‚   β”œβ”€β”€ App.tsx              # Root component
β”‚   β”‚   β”œβ”€β”€ main.tsx             # Application entry
β”‚   β”‚   └── index.css            # Global styles
β”‚   β”œβ”€β”€ public/
β”‚   β”‚   β”œβ”€β”€ favicon.ico
β”‚   β”‚   └── logo.svg
β”‚   β”œβ”€β”€ index.html
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ tsconfig.json
β”‚   β”œβ”€β”€ vite.config.ts
β”‚   └── tailwind.config.js
β”‚
β”œβ”€β”€ docker-compose.yml               # Local development setup
β”œβ”€β”€ Dockerfile                       # Multi-stage build (Spring Boot + React)
└── README.md

πŸš€ Quick Start

Prerequisites

  • Backend: Java 17+, Maven/Gradle
  • Frontend: Node.js 20+, npm
  • Database: PostgreSQL 15+

Local Development

Option 1: Using Docker Compose (Recommended)

cd cloudscan-ui

# Start all services (Spring Boot + React + PostgreSQL)
docker-compose up

# Access the application
# Frontend: http://localhost:3000
# Backend API: http://localhost:8080

Option 2: Manual Setup

1. Start Backend (Spring Boot)

cd backend

# Set up environment variables
cp src/main/resources/application-dev.yml.example src/main/resources/application-dev.yml
# Edit application-dev.yml with database credentials

# Run PostgreSQL (via Docker)
docker run --name postgres-ui \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=cloudscan_ui \
  -p 5432:5432 \
  -d postgres:15

# Build and run Spring Boot
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
# Or with Gradle:
# ./gradlew bootRun --args='--spring.profiles.active=dev'

Backend will be available at http://localhost:8080

2. Start Frontend (React)

cd frontend

# Install dependencies
npm install

# Set up environment variables
cp .env.example .env
# Edit .env with backend URL

# Start development server
npm run dev

Frontend will be available at http://localhost:3000


πŸ”§ Configuration

Backend Configuration (backend/src/main/resources/application.yml)

server:
  port: 8080

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/cloudscan_ui
    username: postgres
    password: postgres
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

# Backend service URLs
cloudscan:
  services:
    orchestrator: http://localhost:9999
    storage: http://localhost:8082
    api-gateway: http://localhost:8080
    websocket: ws://localhost:9090

# JWT configuration
jwt:
  secret: your-secret-key-change-in-production
  expiration: 86400000  # 24 hours in milliseconds

Frontend Configuration (frontend/.env)

# Backend API URL (Spring Boot)
VITE_API_URL=http://localhost:8080

# WebSocket URL
VITE_WS_URL=ws://localhost:9090

# Feature flags
VITE_ENABLE_DARK_MODE=true
VITE_ENABLE_ANALYTICS=false

πŸ“œ Available Scripts

Backend Scripts

# Development
./mvnw spring-boot:run               # Start Spring Boot app
./mvnw clean package                 # Build JAR
./mvnw test                          # Run tests

# With Gradle
./gradlew bootRun                    # Start Spring Boot app
./gradlew build                      # Build JAR
./gradlew test                       # Run tests

Frontend Scripts

# Development
npm run dev              # Start dev server with HMR
npm run build            # Build for production
npm run preview          # Preview production build

# Code Quality
npm run lint             # Run ESLint
npm run type-check       # Run TypeScript compiler check
npm run format           # Format code with Prettier

# Testing
npm run test             # Run unit tests
npm run test:e2e         # Run E2E tests (Playwright)
npm run test:coverage    # Generate coverage report

🎨 Key Features

1. Authentication

Login Page (/login)

  • Email/password login
  • "Remember me" option
  • Password reset link
  • JWT token management

Signup Page (/signup)

  • User registration
  • Organization creation
  • Email verification

2. Dashboard

Overview Page (/dashboard)

  • Recent scans summary
  • Security trends (7/30/90 days)
  • Quick actions
  • Critical findings alerts

3. Scan Management

New Scan Wizard (/scans/new)

  • Step 1: Select project
  • Step 2: Configure source (Git repo, branch)
  • Step 3: Choose scan types (SAST, SCA, Secrets, License)
  • Step 4: Notifications and options

Scan Details (/scans/:id)

  • Real-time progress bar
  • Live log streaming (WebSocket)
  • Step-by-step execution status
  • Cancel scan button

Scan Results (/scans/:id/findings)

  • Findings table with filters
    • By severity (critical, high, medium, low)
    • By type (SAST, SCA, secrets, license)
    • By status (new, fixed, ignored)
  • Finding detail modal
    • Vulnerable code snippet
    • Remediation steps
    • References (CWE, CVE, OWASP)

4. Real-time Updates

Uses WebSocket for:

  • Live scan logs
  • Status changes
  • Progress updates
// Example WebSocket usage
const ws = useWebSocket(`${WS_URL}/scans/${scanId}`, {
  onMessage: (event) => {
    const data = JSON.parse(event.data);
    if (data.type === 'log') {
      appendLog(data.message);
    }
  }
});

🎨 UI Components

Common Components

// Button with variants
<Button variant="primary" size="md" onClick={handleClick}>
  Start Scan
</Button>

// Alert/Toast notifications
<Alert type="success" message="Scan completed!" />

// Modal dialog
<Modal isOpen={isOpen} onClose={onClose} title="Finding Details">
  <FindingDetails finding={selectedFinding} />
</Modal>

// Loading spinner
<Spinner size="lg" />

// Progress bar
<ProgressBar value={60} max={100} label="60% complete" />

Scan Components

// Scan status badge
<ScanStatusBadge status="running" />  // Shows colored badge

// Finding severity icon
<SeverityIcon severity="critical" />  // Red icon

// Live logs viewer
<LogViewer logs={logs} isStreaming={true} />

// Findings table
<FindingsTable findings={findings} onSelect={handleSelect} />

πŸ§ͺ Testing

Unit Tests (Vitest)

# Run tests
npm run test

# Watch mode
npm run test:watch

E2E Tests (Playwright)

# Install Playwright
npx playwright install

# Run E2E tests
npm run test:e2e

# Run specific test
npx playwright test tests/scan-flow.spec.ts

Example E2E test:

test('create and monitor scan', async ({ page }) => {
  await page.goto('/login');
  await page.fill('[name=email]', '[email protected]');
  await page.fill('[name=password]', 'password');
  await page.click('button[type=submit]');

  await page.goto('/scans/new');
  await page.selectOption('[name=project]', 'my-project');
  await page.click('button:has-text("Start Scan")');

  await expect(page.locator('.scan-status')).toHaveText(/running/i);
});

🎨 Styling Guide

Uses TailwindCSS with custom theme:

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          500: '#3b82f6',
          600: '#2563eb',
          900: '#1e3a8a',
        },
        severity: {
          critical: '#dc2626',  // Red
          high: '#ea580c',      // Orange
          medium: '#ca8a04',    // Yellow
          low: '#2563eb',       // Blue
          info: '#6b7280',      // Gray
        }
      }
    }
  }
}

Usage:

<div className="bg-primary-500 text-white px-4 py-2 rounded-lg">
  Critical Finding
</div>

<span className="text-severity-critical font-bold">
  CRITICAL
</span>

🌐 API Integration

API Client Setup

// src/services/api.ts
import axios from 'axios';

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL,
});

// Add auth token to requests
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

export default api;

Example API Calls

// src/services/scans.ts
import api from './api';

export const scansService = {
  // Create scan
  create: async (data: CreateScanRequest) => {
    const response = await api.post('/api/v1/scans', data);
    return response.data;
  },

  // Get scan details
  getById: async (id: string) => {
    const response = await api.get(`/api/v1/scans/${id}`);
    return response.data;
  },

  // List scans
  list: async (filters?: ScanFilters) => {
    const response = await api.get('/api/v1/scans', { params: filters });
    return response.data;
  },
};

React Query Integration

// src/hooks/useScans.ts
import { useQuery } from '@tanstack/react-query';
import { scansService } from '../services/scans';

export const useScans = () => {
  return useQuery({
    queryKey: ['scans'],
    queryFn: () => scansService.list(),
    refetchInterval: 10000, // Auto-refresh every 10s
  });
};

export const useScanDetails = (scanId: string) => {
  return useQuery({
    queryKey: ['scans', scanId],
    queryFn: () => scansService.getById(scanId),
    enabled: !!scanId,
  });
};

🐳 Docker

Build

docker build -t cloudscan/ui:latest .

Run

docker run -p 3000:80 \
  -e VITE_API_URL=http://api-gateway:8080 \
  cloudscan/ui:latest

Dockerfile

# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

πŸ“± Responsive Design

Breakpoints:

  • sm: 640px (mobile)
  • md: 768px (tablet)
  • lg: 1024px (desktop)
  • xl: 1280px (large desktop)

Example:

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  {/* Responsive grid */}
</div>

🀝 Contributing

  1. Follow the component structure
  2. Use TypeScript for all new code
  3. Add tests for new features
  4. Follow Airbnb style guide
  5. Use Prettier for formatting

πŸ“„ License

Apache 2.0 - See LICENSE

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published