Aethonix AI

byPraveen Kannan

What model are you

LandingAdmin MessagesAdmin LoginAdmin Payments
Landing

Comments (0)

No comments yet. Be the first!

System Requirements

System Requirement Document
Page 1 of 32

System Requirements Document

Project Overview

Project Name

AETHONIX

Version

1.0.0

Vision Statement

AETHONIX is a futuristic AI SaaS platform designed to feel like the marketing site of a $1B-valuation startup. It blends cinematic 3D visuals, neon glassmorphism, and a fully functional admin backend into a single, deployable Next.js 15 application.

Key Features

  • Cinematic 3D Hero: Floating metallic "A" logo with orbit rings, particles, and bloom lighting using React Three Fiber.
  • Glassmorphism UI: Frosted-glass cards with neon purple borders and soft glow.
  • Smooth Scroll: Lenis-powered buttery scrolling.
  • Animated Sections: Framer Motion + GSAP scroll reveals on every block.
  • Full Admin Dashboard: JWT-protected, with analytics, content management, and uploads.
  • MongoDB Backend: Persistent storage for contact, feedback, projects, payments, and resumes.
  • Mock Data Fallback: Site renders perfectly even without a database connection.
  • Cloudinary Uploads: Payment proofs and resume PDFs hosted on a CDN.
  • Portfolio Integration: Praveen Kannan's portfolio link surfaced in 10+ strategic locations.
  • Mobile-First & SEO-Optimized: Lighthouse scores ≥ 90.
Page 2 of 32

Target Experience

The visitor should feel they've entered a holographic command center: dark cosmic backgrounds, soft purple glows pulsing in the periphery, a 3D logo orbiting in space, and content that fades and floats into view as they scroll. Every interaction — hover, click, scroll — should feel premium, intentional, and weightless.

Tech Stack

Page 3 of 32

Technologies

  • Next.js: ^15.0.0
  • React: ^18.3.0
  • React DOM: ^18.3.0
  • TypeScript: ^5.4.0
  • Tailwind CSS: ^3.4.0
  • Framer Motion: ^11.0.0
  • GSAP: ^3.12.0
  • Three.js: ^0.165.0
  • React Three Fiber: @react-three/fiber ^8.16.0
  • React Three Drei: @react-three/drei ^9.108.0
  • React Three Postprocessing: @react-three/postprocessing ^2.16.0
  • Swiper: ^11.0.0
  • Lenis: ^1.1.0
  • Zustand: ^4.5.0
  • Mongoose: ^8.5.0
  • JWT: jsonwebtoken ^9.0.0
  • bcryptjs: ^2.4.3
  • Cloudinary: ^2.4.0
  • Lucide React: ^0.400.0
  • Recharts: ^2.12.0
  • Shadcn/ui: latest
  • Clsx: ^2.1.0
  • Tailwind Merge: ^2.4.0
  • Class Variance Authority: ^0.7.0

Design System & Visual Identity

Page 4 of 32

Color Palette

  • Primary Background: #050816
  • Section Alt Backgrounds: #0a0a1a
  • Primary Purple: #7c3aed
  • Hover States, Glows: #a855f7
  • Highlighted Text: #c084fc
  • Secondary Accent: #22d3ee
  • Glassmorphism Fill: rgba(255,255,255,0.05)
  • Glass Card Border: rgba(255,255,255,0.10)
  • Headings: #ffffff
  • Body Text: #a1a1aa
  • Captions: #71717a

Typography

  • Headings: Space Grotesk (700, 600)
  • Body: Inter (400, 500)
  • Mono / Code: JetBrains Mono (400)

Signature Gradients

  • Hero Text Gradient: Linear gradient from #c084fc to #7c3aed
  • Button Gradient: Linear gradient from #7c3aed to #a855f7
  • Background Radial Glow: Radial gradient with rgba(124,58,237,0.25)
Page 5 of 32

Glassmorphism Recipe

  • Background: rgba(255, 255, 255, 0.05)
  • Backdrop Filter: blur(20px) saturate(180%)
  • Border: 1px solid rgba(255, 255, 255, 0.10)
  • Border Radius: 1rem
  • Box Shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37)

Glow / Shadow Effects

  • Neon Glow: Box shadow with rgba(168, 85, 247, 0.4)
  • Text Glow: Text shadow with rgba(168, 85, 247, 0.6)

Spacing Scale

  • xs: 0.5rem
  • sm: 1rem
  • md: 1.5rem
  • lg: 3rem
  • xl: 6rem
  • 2xl: 8rem

Project Setup

Page 6 of 32

Setup Commands

  1. Scaffold the Next.js app:
    npx create-next-app@latest aethonix --typescript --tailwind --app --import-alias "@/*"
    
  2. Move into the project:
    cd aethonix
    
  3. Install runtime dependencies:
    npm install framer-motion gsap three @react-three/fiber @react-three/drei \
      @react-three/postprocessing swiper lenis zustand mongoose jsonwebtoken \
      bcryptjs cloudinary lucide-react recharts clsx tailwind-merge \
      class-variance-authority
    
  4. Install dev dependencies:
    npm install -D @types/jsonwebtoken @types/bcryptjs @types/three
    
  5. Initialize shadcn/ui:
    npx shadcn@latest init
    
  6. Add shadcn primitives:
    npx shadcn@latest add button card input textarea label dialog toast
    
  7. Start the dev server:
    npm run dev
    
Page 7 of 32

Environment Variables

Create .env.example at the project root:


# MongoDB
MONGODB_URI=your_mongodb_connection_string_here

# Authentication (JWT)
JWT_SECRET=replace_with_a_strong_32_character_minimum_secret_string
JWT_EXPIRES_IN=7d

# Admin Seed Credentials
ADMIN_EMAIL=admin@aethonix.ai
ADMIN_PASSWORD=Admin@123

# Cloudinary (uploads)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret

# Public
NEXT_PUBLIC_PORTFOLIO_URL=https://praveen-kannan-4607.vercel.app/
NEXT_PUBLIC_SITE_URL=https://aethonix.vercel.app
Page 8 of 32

Folder Structure

aethonix/
├── app/
│   ├── layout.tsx
│   ├── page.tsx
│   ├── globals.css
│   ├── loading.tsx
│   ├── not-found.tsx
│   ├── about/page.tsx
│   ├── solutions/page.tsx
│   ├── products/page.tsx
│   ├── resources/page.tsx
│   ├── contact/page.tsx
│   │
│   ├── admin/
│   │   ├── login/page.tsx
│   │   └── dashboard/
│   │       ├── layout.tsx
│   │       ├── page.tsx
│   │       ├── payments/page.tsx
│   │       ├── feedback/page.tsx
│   │       ├── projects/page.tsx
│   │       ├── messages/page.tsx
│   │       └── resume/page.tsx
│   │
│   └── api/
│       ├── auth/
│       │   ├── login/route.ts
│       │   ├── verify/route.ts
│       │   └── logout/route.ts
│       ├── contact/route.ts
│       ├── feedback/route.ts
│       ├── projects/route.ts
│       ├── payments/route.ts
│       ├── resume/route.ts
│       └── upload/route.ts
│
├── components/
│   ├── layout/
│   │   ├── Navbar.tsx
│   │   ├── Footer.tsx
│   │   ├── ScrollProgress.tsx
│   │   ├── CursorGlow.tsx
│   │   ├── LoadingScreen.tsx
│   │   └── ParticleBg.tsx
│   ├── sections/
│   │   ├── Hero.tsx
│   │   ├── TrustedBrands.tsx
│   │   ├── About.tsx
│   │   ├── Solutions.tsx
│   │   ├── Projects.tsx
│   │   ├── Payments.tsx
│   │   ├── Feedback.tsx
│   │   ├── Resources.tsx
│   │   ├── Contact.tsx
│   │   └── CTABanner.tsx
│   ├── 3d/
│   │   ├── HeroLogo3D.tsx
│   │   ├── OrbitRings.tsx
│   │   └── ParticleField.tsx
│   ├── ui/
│   │   ├── GlowCard.tsx
│   │   ├── NeonButton.tsx
│   │   ├── GradientText.tsx
│   │   ├── StatCard.tsx
│   │   └── button.tsx          # shadcn
│   └── admin/
│       ├── Sidebar.tsx
│       ├── StatCard.tsx
│       ├── DataTable.tsx
│       └── Charts.tsx
│
├── lib/
│   ├── mongodb.ts
│   ├── auth.ts
│   ├── cloudinary.ts
│   ├── utils.ts
│   ├── motion.ts          # framer-motion variants
│   └── mockData.ts
│
├── models/
│   ├── User.ts
│   ├── Contact.ts
│   ├── Feedback.ts
│   ├── Project.ts
│   ├── Payment.ts
│   └── Resume.ts
│
├── store/
│   └── useUIStore.ts
│
├── public/
│   ├── logos/
│   │   ├── google.svg
│   │   ├── microsoft.svg
│   │   ├── nvidia.svg
│   │   ├── aws.svg
│   │   └── meta.svg
│   └── favicon.ico
│
├── middleware.ts
├── tailwind.config.ts
├── next.config.js
├── tsconfig.json
├── .env.example
├── .env.local             # gitignored
└── package.json
Page 9 of 32

Configuration Files

Page 10 of 32

Tailwind Configuration

import type { Config } from "tailwindcss";
const config: Config = {
  darkMode: "class",
  content: [
    "./app/**/*.{ts,tsx}",
    "./components/**/*.{ts,tsx}",
  ],
  theme: {
    extend: {
      colors: {
        bg: {
          DEFAULT: "#050816",
          secondary: "#0a0a1a",
        },
        primary: {
          DEFAULT: "#7c3aed",
          glow: "#a855f7",
          light: "#c084fc",
        },
        accent: {
          cyan: "#22d3ee",
        },
        glass: {
          DEFAULT: "rgba(255,255,255,0.05)",
          border: "rgba(255,255,255,0.10)",
        },
      },
      fontFamily: {
        sans: ["var(--font-inter)", "system-ui"],
        display: ["var(--font-space)", "system-ui"],
        mono: ["JetBrains Mono", "monospace"],
      },
      keyframes: {
        "glow-pulse": {
          "0%, 100%": { boxShadow: "0 0 20px rgba(168,85,247,0.4)" },
          "50%":      { boxShadow: "0 0 40px rgba(168,85,247,0.8)" },
        },
        float: {
          "0%, 100%": { transform: "translateY(0px)" },
          "50%":      { transform: "translateY(-12px)" },
        },
        "gradient-shift": {
          "0%, 100%": { backgroundPosition: "0% 50%" },
          "50%":      { backgroundPosition: "100% 50%" },
        },
        shimmer: {
          "0%":   { backgroundPosition: "-200% 0" },
          "100%": { backgroundPosition: "200% 0" },
        },
      },
      animation: {
        "glow-pulse":   "glow-pulse 3s ease-in-out infinite",
        float:          "float 4s ease-in-out infinite",
        "gradient-shift": "gradient-shift 8s ease infinite",
        shimmer:        "shimmer 2s linear infinite",
      },
      backgroundImage: {
        "radial-glow": "radial-gradient(ellipse at top, rgba(124,58,237,0.25) 0%, transparent 60%)",
        "grid-pattern": "linear-gradient(rgba(168,85,247,0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(168,85,247,0.05) 1px, transparent 1px)",
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
};
export default config;
Page 11 of 32

Global Styles

@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
  :root {
    --bg: #050816;
    --primary: #7c3aed;
    --primary-glow: #a855f7;
  }
  html { scroll-behavior: smooth; }
  body {
    @apply bg-bg text-white antialiased;
    font-family: var(--font-inter);
    background-image:
      radial-gradient(ellipse 80% 50% at 50% -20%, rgba(124,58,237,0.3), transparent),
      radial-gradient(ellipse 60% 50% at 80% 50%, rgba(34,211,238,0.08), transparent);
  }
  h1, h2, h3, h4 { font-family: var(--font-space); @apply font-bold tracking-tight; }
  ::selection { @apply bg-primary/40 text-white; }
}
@layer components {
  .glass {
    @apply bg-white/[0.05] backdrop-blur-xl border border-white/10 rounded-2xl;
    box-shadow: 0 8px 32px 0 rgba(0,0,0,0.37);
  }
  .glass-hover {
    @apply glass transition-all duration-300;
  }
  .glass-hover:hover {
    @apply border-primary-glow/40 -translate-y-1;
    box-shadow: 0 12px 40px rgba(168,85,247,0.25);
  }
  .neon-glow {
    box-shadow:
      0 0 20px rgba(168,85,247,0.4),
      0 0 40px rgba(168,85,247,0.2);
  }
  .gradient-text {
    @apply bg-clip-text text-transparent;
    background-image: linear-gradient(135deg, #c084fc 0%, #a855f7 50%, #7c3aed 100%);
  }
  .gradient-border {
    position: relative;
  }
  .gradient-border::before {
    content: '';
    position: absolute; inset: 0;
    padding: 1px;
    border-radius: inherit;
    background: linear-gradient(135deg, #7c3aed, #22d3ee);
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    pointer-events: none;
  }
  .section-padding { @apply px-6 py-24 md:px-12 lg:px-20; }
}
@layer utilities {
  .no-scrollbar::-webkit-scrollbar { display: none; }
  .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
}
Page 12 of 32

Next.js Configuration

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    remotePatterns: [
      { protocol: "https", hostname: "res.cloudinary.com" },
      { protocol: "https", hostname: "api.dicebear.com" },
      { protocol: "https", hostname: "images.unsplash.com" },
    ],
  },
  experimental: {
    optimizePackageImports: ["lucide-react", "framer-motion"],
  },
};
module.exports = nextConfig;
Page 13 of 32

TypeScript Configuration

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [{ "name": "next" }],
    "paths": { "@/*": ["./*"] }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

Core Library Files

Page 14 of 32

MongoDB Connection

import mongoose from "mongoose";
const MONGODB_URI = process.env.MONGODB_URI;
interface CachedConnection {
  conn: typeof mongoose | null;
  promise: Promise<typeof mongoose> | null;
}
declare global {
  // eslint-disable-next-line no-var
  var mongooseCache: CachedConnection | undefined;
}
const cached: CachedConnection =
  global.mongooseCache ?? { conn: null, promise: null };
export async function connectDB(): Promise<typeof mongoose> {
  if (!MONGODB_URI) {
    throw new Error("MONGODB_URI not configured");
  }
  if (cached.conn) return cached.conn;
  if (!cached.promise) {
    cached.promise = mongoose.connect(MONGODB_URI, {
      bufferCommands: false,
    });
  }
  cached.conn = await cached.promise;
  return cached.conn;
}
Page 15 of 32

Authentication Library

import jwt from "jsonwebtoken";
import bcrypt from "bcryptjs";
const SECRET = process.env.JWT_SECRET || "dev-secret-change-me-32chars-min";
const EXPIRES_IN = process.env.JWT_EXPIRES_IN || "7d";
export interface JWTPayload {
  userId: string;
  email: string;
  role: "admin" | "user";
}
export const signToken = (payload: JWTPayload): string =>
  jwt.sign(payload, SECRET, { expiresIn: EXPIRES_IN });
export const verifyToken = (token: string): JWTPayload | null => {
  try {
    return jwt.verify(token, SECRET) as JWTPayload;
  } catch {
    return null;
  }
};
export const hashPassword = (plain: string): Promise<string> =>
  bcrypt.hash(plain, 10);
export const comparePassword = (
  plain: string,
  hashed: string
): Promise<boolean> => bcrypt.compare(plain, hashed);
Page 16 of 32

Cloudinary Library

import { v2 as cloudinary } from "cloudinary";
cloudinary.config({
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
  api_key:    process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
});
export async function uploadToCloudinary(
  fileBuffer: Buffer,
  folder = "aethonix"
): Promise<string> {
  return new Promise((resolve, reject) => {
    cloudinary.uploader
      .upload_stream({ folder, resource_type: "auto" }, (err, result) => {
        if (err || !result) return reject(err);
        resolve(result.secure_url);
      })
      .end(fileBuffer);
  });
}
export default cloudinary;
Page 17 of 32

Utility Functions

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Mongoose Models

User Model

import mongoose, { Schema, model, models } from "mongoose";
const UserSchema = new Schema(
  {
    email:    { type: String, required: true, unique: true, lowercase: true },
    password: { type: String, required: true },
    role:     { type: String, enum: ["admin", "user"], default: "user" },
  },
  { timestamps: true }
);
export default models.User || model("User", UserSchema);
Page 18 of 32

Contact Model

import { Schema, model, models } from "mongoose";
const ContactSchema = new Schema(
  {
    name:    { type: String, required: true },
    email:   { type: String, required: true },
    subject: { type: String, required: true },
    message: { type: String, required: true },
  },
  { timestamps: true }
);
export default models.Contact || model("Contact", ContactSchema);

Feedback Model

import { Schema, model, models } from "mongoose";
const FeedbackSchema = new Schema(
  {
    name:    { type: String, required: true },
    email:   { type: String, required: true },
    type:    { type: String, enum: ["general", "bug", "feature", "praise"], default: "general" },
    rating:  { type: Number, min: 1, max: 5, required: true },
    message: { type: String, required: true, maxlength: 1000 },
  },
  { timestamps: true }
);
export default models.Feedback || model("Feedback", FeedbackSchema);
Page 19 of 32

Project Model

import { Schema, model, models } from "mongoose";
const ProjectSchema = new Schema(
  {
    title:       { type: String, required: true },
    category:    { type: String, enum: ["ai-ml", "web", "dashboard", "automation", "system"], required: true },
    description: { type: String, required: true },
    image:       { type: String, required: true },
    tech:        { type: [String], default: [] },
    link:        { type: String, default: "" },
    featured:    { type: Boolean, default: false },
  },
  { timestamps: true }
);
export default models.Project || model("Project", ProjectSchema);
Page 20 of 32

Payment Model

import { Schema, model, models } from "mongoose";
const PaymentSchema = new Schema(
  {
    clientName:    { type: String, required: true },
    amount:        { type: Number, required: true },
    transactionId: { type: String, required: true, unique: true },
    date:          { type: Date, default: Date.now },
    status:        { type: String, enum: ["pending", "verified", "failed"], default: "verified" },
    proofUrl:      { type: String, default: "" },
  },
  { timestamps: true }
);
export default models.Payment || model("Payment", PaymentSchema);

Resume Model

import { Schema, model, models } from "mongoose";
const ResumeSchema = new Schema(
  {
    url:        { type: String, required: true },
    version:    { type: String, default: "v1.0" },
    uploadedAt: { type: Date, default: Date.now },
  },
  { timestamps: true }
);
export default models.Resume || model("Resume", ResumeSchema);
Page 21 of 32

API Routes

Authentication Routes

Page 22 of 32

Login Route

import { NextResponse } from "next/server";
import { cookies } from "next/headers";
import { connectDB } from "@/lib/mongodb";
import { signToken, comparePassword, hashPassword } from "@/lib/auth";
import User from "@/models/User";
export async function POST(req: Request) {
  const { email, password } = await req.json();
  try {
    await connectDB();
    let user = await User.findOne({ email });
    // Seed admin on first request if missing
    if (!user && email === process.env.ADMIN_EMAIL) {
      const hashed = await hashPassword(process.env.ADMIN_PASSWORD!);
      user = await User.create({ email, password: hashed, role: "admin" });
    }
    if (!user) return NextResponse.json({ error: "Invalid credentials" }, { status: 401 });
    const ok = await comparePassword(password, user.password);
    if (!ok) return NextResponse.json({ error: "Invalid credentials" }, { status: 401 });

    const token = signToken({ userId: user._id.toString(), email: user.email, role: user.role });
    cookies().set("aethonix_token", token, {
      httpOnly: true, secure: true, sameSite: "lax", maxAge: 60 * 60 * 24 * 7, path: "/",
    });
    return NextResponse.json({ success: true, user: { email: user.email, role: user.role } });
  } catch {
    // Demo fallback when no MongoDB
    if (email === "admin@aethonix.ai" && password === "Admin@123") {
      const token = signToken({ userId: "demo", email, role: "admin" });
      cookies().set("aethonix_token", token, { httpOnly: true, secure: true, sameSite: "lax", maxAge: 604800, path: "/" });
      return NextResponse.json({ success: true, mock: true });
    }
    return NextResponse.json({ error: "Invalid credentials" }, { status: 401 });
  }
}
Page 23 of 32

Verify Route

import { NextResponse } from "next/server";
import { cookies } from "next/headers";
import { verifyToken } from "@/lib/auth";
export async function GET() {
  const token = cookies().get("aethonix_token")?.value;
  if (!token) return NextResponse.json({ authenticated: false }, { status: 401 });
  const payload = verifyToken(token);
  if (!payload) return NextResponse.json({ authenticated: false }, { status: 401 });
  return NextResponse.json({ authenticated: true, user: payload });
}
Page 24 of 32

Contact API

import { NextResponse } from "next/server";
import { connectDB } from "@/lib/mongodb";
import Contact from "@/models/Contact";
export async function POST(req: Request) {
  const body = await req.json();
  try {
    await connectDB();
    const doc = await Contact.create(body);
    return NextResponse.json({ success: true, data: doc });
  } catch {
    return NextResponse.json({ success: true, mock: true });
  }
}
export async function GET() {
  try {
    await connectDB();
    const data = await Contact.find().sort({ createdAt: -1 }).limit(50);
    return NextResponse.json({ data });
  } catch {
    return NextResponse.json({ data: [], mock: true });
  }
}
Page 25 of 32

Feedback API

import { NextResponse } from "next/server";
import { connectDB } from "@/lib/mongodb";
import Feedback from "@/models/Feedback";
import { MOCK_FEEDBACK } from "@/lib/mockData";
export async function POST(req: Request) {
  const body = await req.json();
  try {
    await connectDB();
    const doc = await Feedback.create(body);
    return NextResponse.json({ success: true, data: doc });
  } catch {
    return NextResponse.json({ success: true, mock: true });
  }
}
export async function GET() {
  try {
    await connectDB();
    const data = await Feedback.find().sort({ createdAt: -1 });
    return NextResponse.json({ data });
  } catch {
    return NextResponse.json({ data: MOCK_FEEDBACK, mock: true });
  }
}
Page 26 of 32

Projects API

import { NextResponse } from "next/server";
import { connectDB } from "@/lib/mongodb";
import Project from "@/models/Project";
import { MOCK_PROJECTS } from "@/lib/mockData";
export async function GET() {
  try {
    await connectDB();
    const data = await Project.find().sort({ createdAt: -1 });
    return NextResponse.json({ data: data.length ? data : MOCK_PROJECTS });
  } catch {
    return NextResponse.json({ data: MOCK_PROJECTS, mock: true });
  }
}
export async function POST(req: Request) {
  const body = await req.json();
  try {
    await connectDB();
    const doc = await Project.create(body);
    return NextResponse.json({ success: true, data: doc });
  } catch {
    return NextResponse.json({ error: "DB unavailable" }, { status: 503 });
  }
}
Page 27 of 32

Payments API

import { NextResponse } from "next/server";
import { connectDB } from "@/lib/mongodb";
import Payment from "@/models/Payment";
import { MOCK_PAYMENTS } from "@/lib/mockData";
export async function GET() {
  try {
    await connectDB();
    const data = await Payment.find().sort({ date: -1 });
    return NextResponse.json({ data: data.length ? data : MOCK_PAYMENTS });
  } catch {
    return NextResponse.json({ data: MOCK_PAYMENTS, mock: true });
  }
}
export async function POST(req: Request) {
  const body = await req.json();
  try {
    await connectDB();
    const doc = await Payment.create(body);
    return NextResponse.json({ success: true, data: doc });
  } catch {
    return NextResponse.json({ error: "DB unavailable" }, { status: 503 });
  }
}
Page 28 of 32

Resume API

import { NextResponse } from "next/server";
import { connectDB } from "@/lib/mongodb";
import Resume from "@/models/Resume";
export async function GET() {
  try {
    await connectDB();
    const latest = await Resume.findOne().sort({ uploadedAt: -1 });
    return NextResponse.json({ data: latest });
  } catch {
    return NextResponse.json({ data: { url: "/resume-praveen.pdf", version: "v1.0", mock: true } });
  }
}
export async function POST(req: Request) {
  const body = await req.json();
  try {
    await connectDB();
    const doc = await Resume.create(body);
    return NextResponse.json({ success: true, data: doc });
  } catch {
    return NextResponse.json({ error: "DB unavailable" }, { status: 503 });
  }
}
Page 29 of 32

Upload API

import { NextResponse } from "next/server";
import { uploadToCloudinary } from "@/lib/cloudinary";
export async function POST(req: Request) {
  try {
    const formData = await req.formData();
    const file = formData.get("file") as File | null;
    if (!file) return NextResponse.json({ error: "No file" }, { status: 400 });
    const buffer = Buffer.from(await file.arrayBuffer());
    const url = await uploadToCloudinary(buffer);
    return NextResponse.json({ success: true, url });
  } catch (err) {
    return NextResponse.json({ error: "Upload failed" }, { status: 500 });
  }
}

Middleware

Page 30 of 32

Admin Route Protection

import { NextResponse, type NextRequest } from "next/server";
import { verifyToken } from "@/lib/auth";
export function middleware(req: NextRequest) {
  const { pathname } = req.nextUrl;
  // Only guard /admin/dashboard/*
  if (pathname.startsWith("/admin/dashboard")) {
    const token = req.cookies.get("aethonix_token")?.value;
    const payload = token ? verifyToken(token) : null;
    if (!payload || payload.role !== "admin") {
      const loginUrl = new URL("/admin/login", req.url);
      return NextResponse.redirect(loginUrl);
    }
  }
  return NextResponse.next();
}

export const config = {
  matcher: ["/admin/dashboard/:path*"],
};

Page Sections — Component Map

Page 31 of 32

Section and Component File Mapping

  • Loading Screen: components/layout/LoadingScreen.tsx
  • Cursor Glow: components/layout/CursorGlow.tsx
  • Scroll Progress: components/layout/ScrollProgress.tsx
  • Particle Background: components/layout/ParticleBg.tsx
  • Navbar: components/layout/Navbar.tsx
  • Hero: components/sections/Hero.tsx
  • Hero 3D Logo: components/3d/HeroLogo3D.tsx
  • Trusted Brands: components/sections/TrustedBrands.tsx
  • About: components/sections/About.tsx
  • Solutions: components/sections/Solutions.tsx
  • Projects: components/sections/Projects.tsx
  • Payment Proofs: components/sections/Payments.tsx
  • Feedback: components/sections/Feedback.tsx
  • Resources: components/sections/Resources.tsx
  • Contact: components/sections/Contact.tsx
  • CTA Banner: components/sections/CTABanner.tsx
  • Footer: components/layout/Footer.tsx

Key Component Implementations

Navbar

  • File: components/layout/Navbar.tsx
  • Description: Implements a sticky navigation bar with a mobile menu toggle.
Page 32 of 32

Hero Section

  • File: components/sections/Hero.tsx
  • Description: Features a left-aligned text block and a right-aligned 3D logo.

License

MIT

Portfolio Owner

Praveen Kannan

Portfolio URL

Praveen Kannan's Portfolio

Aesthetic

Dark Neon Cyberpunk · Futuristic AI SaaS

Last Updated

2025

Landing: View Site
Admin Login: Sign In
Admin Dashboard: View Analytics
Admin Dashboard: View Messages
Admin Messages: Manage Contacts
Admin Payments: View Records
Admin Payments: Verify Transactions
Admin Feedback: Review Ratings
Admin Projects: Manage Portfolio
Admin Projects: Add Project
Admin Resume: Upload Resume