Skip to content

Repository files navigation

ModelBoard - AI Portfolio as a Service

ModelBoard TypeScript Supabase Tailwind CSS HuggingFace

ModelBoard is a modern, full-featured platform for hosting, showcasing, and testing AI models β€” similar to Hugging Face. Built with Next.js 15, TypeScript, Supabase, and real ML inference via HuggingFace API.

✨ Features

Core Features

  • 🏠 Landing Page: Beautiful, responsive home page with feature highlights and call-to-action
  • πŸ” Model Discovery: Browse and search AI models with advanced tag filtering
  • πŸ“ Model Details: Comprehensive model pages with metadata, tags, and statistics
  • πŸ” Google SSO: Secure authentication via Google OAuth with session persistence
  • πŸ‘€ User Dashboard: Full CRUD functionality for managing your AI models
  • πŸ“± Fully Responsive: Optimized experience across desktop, tablet, and mobile
  • πŸŒ™ Dark Mode: Built-in dark mode support throughout the app

Advanced Features πŸš€

  • 🎯 Interactive Model Demos: Test models directly in the browser with real-time inference
  • πŸ€– HuggingFace Integration: Real ML model inference (not mocked!)
    • Text-to-Text (Summarization)
    • Image-to-Text (Image Captioning)
    • Text-to-Image (Stable Diffusion)
    • Sentiment Analysis
    • Question Answering
  • οΏ½ Public Portfolio Pages: Share your work at modelboard.app/username
  • πŸ”’ Public/Private Models: Control visibility of your models
  • πŸ“€ File Upload Support: Upload preview images and model files directly to Supabase Storage
  • πŸ”— External URLs: Alternative option to use external image/file URLs
  • πŸ““ Notebook Integration: Link to Google Colab or Jupyter notebooks
  • 🎨 Custom API Endpoints: Override default models with your own

🚒 Deployment

Vercel Deployment

  1. Push your code to GitHub

  2. Import the project in Vercel

  3. Add environment variables:

    • NEXT_PUBLIC_SUPABASE_URL
    • NEXT_PUBLIC_SUPABASE_ANON_KEY
    • HUGGINGFACE_API_TOKEN
  4. Deploy!

Important Configuration

Ensure your next.config.ts includes image domains:

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: '*.supabase.co',
      },
      {
        protocol: 'https',
        hostname: 'lh3.googleusercontent.com',
      },
      {
        protocol: 'https',
        hostname: 'huggingface.co',
      },
      {
        protocol: 'https',
        hostname: 'images.unsplash.com',
      },
    ],
  },
};

Post-Deployment

After deploying to Vercel:

  1. Update Google OAuth redirect URI with your Vercel domain
  2. Verify Supabase storage buckets are created
  3. Test file upload functionality
  4. Confirm RLS policies are active

🎯 Usage Examples

Creating a Model with File Upload

  1. Sign in with Google
  2. Navigate to "My Account"
  3. Fill in model details:
    • Title, description, tags
    • Upload preview image (max 5MB) OR paste external URL
    • Upload model file (max 500MB) OR paste external URL
    • Add notebook URL (Google Colab/Jupyter)
    • Select demo type (text-to-text, image-to-text, etc.)
    • Optionally add custom API endpoint
    • Toggle public/private visibility
  4. Click "Add Model"

Testing a Model Demo

  1. Navigate to any model detail page
  2. Scroll to the "Demo" section
  3. Interact based on demo type:
    • Text-to-Text: Enter text to summarize
    • Image-to-Text: Upload an image for captioning
    • Text-to-Image: Enter a prompt to generate an image
    • Sentiment Analysis: Enter text to analyze sentiment
    • Question Answering: Provide context and ask a question
  4. View real-time results

Sharing Your Portfolio

Your public models are automatically available at:

https://modelboard.app/your-username

Share this link to showcase your AI work!

πŸ“Š Database Schema Reference

The models table includes the following fields:

Column Type Description
id UUID Primary key
user_id UUID Foreign key to auth.users
title TEXT Model name
description TEXT Model description
tags TEXT[] Array of tags
preview_image TEXT External image URL
preview_image_path TEXT Supabase Storage path
model_file_path TEXT Supabase Storage path for model files
notebook_url TEXT Link to Google Colab/Jupyter
demo_type TEXT One of: text-to-text, image-to-text, text-to-image, sentiment-analysis, question-answering
api_endpoint TEXT Custom HuggingFace model endpoint
is_public BOOLEAN Public/private visibility
created_at TIMESTAMP Creation timestamp
updated_at TIMESTAMP Last update timestamp

πŸ—„οΈ Supabase Storage Buckets

Two storage buckets are required:

  1. model-previews: For preview images (max 5MB, public access)
  2. model-files: For model files (max 500MB, authenticated access)

Files are organized in user-specific folders: {userId}/{filename}

πŸš€ Getting Started

Prerequisites

  • Node.js 18+ and npm/yarn
  • A Supabase account (sign up here)
  • Google OAuth credentials (from Google Cloud Console)
  • HuggingFace API token (free at huggingface.co)

Installation

  1. Clone the repository:

    git clone https://github.com/Shree-212/ModelBoard.git
    cd ModelBoard
  2. Install dependencies:

    npm install
    # or
    yarn install
  3. Set up environment variables:

    Create a .env.local file:

    NEXT_PUBLIC_SUPABASE_URL=your-project-url
    NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
    HUGGINGFACE_API_TOKEN=your-hf-token
  4. Run database migrations:

    In your Supabase SQL Editor, run these migrations in order:

    a. Create profiles table:

    -- From migrations/create_profiles_table.sql (if not exists)
    CREATE TABLE IF NOT EXISTS public.profiles (
      id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
      username TEXT UNIQUE,
      full_name TEXT,
      avatar_url TEXT,
      bio TEXT,
      created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
    );
    
    -- Auto-create profile on user signup
    CREATE OR REPLACE FUNCTION public.handle_new_user()
    RETURNS TRIGGER AS $$
    BEGIN
      INSERT INTO public.profiles (id, username, full_name, avatar_url)
      VALUES (
        NEW.id,
        COALESCE(NEW.raw_user_meta_data->>'preferred_username', NEW.email),
        COALESCE(NEW.raw_user_meta_data->>'full_name', NEW.raw_user_meta_data->>'name'),
        NEW.raw_user_meta_data->>'avatar_url'
      );
      RETURN NEW;
    END;
    $$ LANGUAGE plpgsql SECURITY DEFINER;
    
    CREATE TRIGGER on_auth_user_created
      AFTER INSERT ON auth.users
      FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();

    b. Add storage path columns:

    -- From migrations/add_storage_paths.sql
    ALTER TABLE public.models
    ADD COLUMN IF NOT EXISTS preview_image_path TEXT,
    ADD COLUMN IF NOT EXISTS model_file_path TEXT,
    ADD COLUMN IF NOT EXISTS notebook_url TEXT,
    ADD COLUMN IF NOT EXISTS demo_type TEXT,
    ADD COLUMN IF NOT EXISTS api_endpoint TEXT,
    ADD COLUMN IF NOT EXISTS is_public BOOLEAN DEFAULT true;

    c. Create storage buckets:

    -- From migrations/create_storage_buckets.sql
    INSERT INTO storage.buckets (id, name, public)
    VALUES 
      ('model-previews', 'model-previews', true),
      ('model-files', 'model-files', false)
    ON CONFLICT (id) DO NOTHING;
    
    -- RLS policies for model-previews
    DROP POLICY IF EXISTS "Users can upload preview images" ON storage.objects;
    CREATE POLICY "Users can upload preview images"
    ON storage.objects FOR INSERT
    WITH CHECK (bucket_id = 'model-previews' AND auth.uid()::text = (storage.foldername(name))[1]);
    
    DROP POLICY IF EXISTS "Users can update their preview images" ON storage.objects;
    CREATE POLICY "Users can update their preview images"
    ON storage.objects FOR UPDATE
    USING (bucket_id = 'model-previews' AND auth.uid()::text = (storage.foldername(name))[1]);
    
    DROP POLICY IF EXISTS "Users can delete their preview images" ON storage.objects;
    CREATE POLICY "Users can delete their preview images"
    ON storage.objects FOR DELETE
    USING (bucket_id = 'model-previews' AND auth.uid()::text = (storage.foldername(name))[1]);
    
    DROP POLICY IF EXISTS "Public preview images are publicly accessible" ON storage.objects;
    CREATE POLICY "Public preview images are publicly accessible"
    ON storage.objects FOR SELECT
    USING (bucket_id = 'model-previews');
    
    -- RLS policies for model-files
    DROP POLICY IF EXISTS "Users can upload model files" ON storage.objects;
    CREATE POLICY "Users can upload model files"
    ON storage.objects FOR INSERT
    WITH CHECK (bucket_id = 'model-files' AND auth.uid()::text = (storage.foldername(name))[1]);
    
    DROP POLICY IF EXISTS "Users can update their model files" ON storage.objects;
    CREATE POLICY "Users can update their model files"
    ON storage.objects FOR UPDATE
    USING (bucket_id = 'model-files' AND auth.uid()::text = (storage.foldername(name))[1]);
    
    DROP POLICY IF EXISTS "Users can delete their model files" ON storage.objects;
    CREATE POLICY "Users can delete their model files"
    ON storage.objects FOR DELETE
    USING (bucket_id = 'model-files' AND auth.uid()::text = (storage.foldername(name))[1]);
    
    DROP POLICY IF EXISTS "Authenticated users can download model files" ON storage.objects;
    CREATE POLICY "Authenticated users can download model files"
    ON storage.objects FOR SELECT
    USING (bucket_id = 'model-files' AND auth.role() = 'authenticated');
  5. Configure Google OAuth:

    • Go to Google Cloud Console
    • Create OAuth 2.0 credentials
    • Add authorized redirect URI: https://your-project.supabase.co/auth/v1/callback
    • Add credentials to Supabase Dashboard β†’ Authentication β†’ Providers β†’ Google
  6. Start the development server:

    npm run dev
    # or
    yarn dev

    Open http://localhost:3000 in your browser.

πŸ—οΈ Project Structure

ModelBoard/
β”œβ”€β”€ app/                          # Next.js App Router
β”‚   β”œβ”€β”€ page.tsx                  # Landing page
β”‚   β”œβ”€β”€ layout.tsx                # Root layout with AuthProvider
β”‚   β”œβ”€β”€ globals.css               # Global styles
β”‚   β”œβ”€β”€ models/                   # Model routes
β”‚   β”‚   β”œβ”€β”€ page.tsx              # Model discovery page
β”‚   β”‚   └── [id]/                 # Dynamic model detail pages
β”‚   β”‚       └── page.tsx
β”‚   β”œβ”€β”€ my-account/               # User dashboard
β”‚   β”‚   └── page.tsx              # CRUD interface with file uploads
β”‚   β”œβ”€β”€ [username]/               # Dynamic portfolio pages
β”‚   β”‚   └── page.tsx              # Public user profiles
β”‚   β”œβ”€β”€ auth/
β”‚   β”‚   └── callback/             # OAuth callback handler
β”‚   β”‚       └── route.ts
β”‚   └── api/
β”‚       └── inference/            # HuggingFace API proxy
β”‚           └── route.ts
β”œβ”€β”€ components/                   # Reusable components
β”‚   β”œβ”€β”€ Navbar.tsx                # Navigation with auth state
β”‚   └── ModelDemo.tsx             # Interactive demo widget
β”œβ”€β”€ contexts/
β”‚   └── AuthContext.tsx           # Global auth state
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ supabase.ts               # Supabase client setup
β”‚   β”œβ”€β”€ storage.ts                # File upload utilities
β”‚   └── utils.ts                  # Helper functions
β”œβ”€β”€ migrations/                   # SQL migration scripts
β”‚   β”œβ”€β”€ add_storage_paths.sql    # Add new columns
β”‚   └── create_storage_buckets.sql # Setup storage
└── next.config.ts                # Next.js config with image domains

🎨 Key Components

ModelDemo Component (/components/ModelDemo.tsx)

Interactive demo widget that dynamically renders UI based on demo_type:

  • text-to-text: Text input β†’ Summarization output
  • image-to-text: Image upload β†’ Caption output
  • text-to-image: Text prompt β†’ Generated image
  • sentiment-analysis: Text input β†’ Sentiment score/label
  • question-answering: Context + Question β†’ Answer

Features:

  • Real-time inference via HuggingFace API
  • Loading states and error handling
  • Visual output rendering (images, formatted text)
  • Responsive design

Storage Utilities (/lib/storage.ts)

File upload helper functions:

  • uploadFile(): Upload files to Supabase Storage
  • deleteFile(): Remove files from storage
  • updateFile(): Replace existing files
  • validateImageFile(): Check image type/size (max 5MB)
  • validateModelFile(): Check model file size (max 500MB)

Portfolio Pages (/app/[username]/page.tsx)

Dynamic routes for public user profiles:

  • Display user info (avatar, bio, username)
  • Grid of public models only (is_public = true)
  • Shareable URLs: modelboard.app/username
  • SEO-friendly with metadata

οΏ½ Database Schema Reference

Models Table

CREATE TABLE models (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES auth.users(id),
  title VARCHAR(255) NOT NULL,
  description TEXT NOT NULL,
  tags TEXT[] DEFAULT ARRAY[]::TEXT[],
  preview_image TEXT,                  -- External URL
  preview_image_path TEXT,             -- Storage path
  model_file_path TEXT,                -- Storage path for model files
  notebook_url TEXT,                   -- Colab/Jupyter link
  demo_type TEXT,                      -- Demo type identifier
  api_endpoint TEXT,                   -- Custom HuggingFace endpoint
  is_public BOOLEAN DEFAULT true,      -- Visibility control
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  likes_count INTEGER DEFAULT 0,
  downloads_count INTEGER DEFAULT 0
);

For complete database setup, see SUPABASE_SETUP.md.

πŸ” Authentication

ModelBoard uses Supabase Authentication with Google OAuth via @supabase/ssr:

  1. Users click "Sign In with Google"
  2. Google OAuth flow redirects to /auth/callback
  3. Session is established and persisted using cookies
  4. Authenticated users gain access to "My Account" and private features

Key Authentication Features

  • Session persistence across page refreshes
  • Server-side and client-side auth utilities
  • Automatic profile creation on user signup
  • Global scope logout (signs out from all devices)

Setting Up Google OAuth

  1. Create OAuth credentials in Google Cloud Console
  2. Configure Supabase with your Client ID and Secret
  3. Add authorized redirect URIs
  4. Add prompt: 'consent' in OAuth options for proper logout

Detailed instructions: SUPABASE_SETUP.md

🎨 Customization

Styling

ModelBoard uses Tailwind CSS for styling. Customize the theme in tailwind.config.ts:

theme: {
  extend: {
    colors: {
      // Add your custom colors
    },
  },
}

Adding New Features

To add new features:

  1. Create new pages in the app/ directory
  2. Add components in components/
  3. Update Supabase schema if needed
  4. Update RLS policies for security
  5. Add new demo types in ModelDemo.tsx if needed

🚒 Deployment (Legacy - See Above for Full Instructions)

Quick Deploy to Vercel

  1. Push your code to GitHub
  2. Import the project to Vercel
  3. Add environment variables in Vercel dashboard
  4. Update Google OAuth redirect URIs with your production URL
  5. Update Supabase Site URL and Redirect URLs

Environment Variables for Production

NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
HUGGINGFACE_API_TOKEN=your_hf_token_here

🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“ License

This project is licensed under the ISC License.

πŸ™ Acknowledgments

πŸ“§ Support

For issues and questions:


Built with ❀️ by Shree

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages