# Project export: commit

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: tinder for founders.
- Devpost: https://devpost.com/software/commit-pr2bzl
- GitHub: https://github.com/jonathanycai/commit
- Demo: https://www.figma.com/design/xff3xXS3H0Q6HK77kwsIXo/CalHacks-2025?node-id=132-358&t=93oRGS9NDtGAkEhw-1
- Video: https://www.youtube.com/embed/eLTScJcsmto?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — Jonathan Cai (40 commits), kashgarg (20 commits), Angela Cheng (10 commits), Copilot (1 commits)

## Devpost submission (written by the team)

### Inspiration

We’ve all been there, sitting on an idea that could be huge, but no one to build it with. Or stuck wanting to make something, but not knowing what. commit was born from that gap: A space where builders, designers, and dreamers can actually find each other. Not another networking app, but a platform built for people who want to ship.

### What it does

commit connects people who want to build projects together. Creators can post ideas, from side projects to hackathon prototypes, and collaborators can swipe through, find something worth building, and join the team. Think Tinder for builders, but instead of dates, you’re matching with your next co-founder.

### How we built it

We built commit using React, TypeScript, Supabase, and Tailwind. The front end handles the swipe interactions and profile cards, while Supabase manages user authentication and real-time project data. We prototyped the UI in Figma, focusing on fast onboarding and swipe-based discovery that feels intuitive but purposeful with minimal clutter.

### Challenges we ran into

Defining the workflow between creators and collaborators. Balancing simplicity and utility: Deciding how much info to show on each card, how to handle project requests, and coming up with an effective SQL schema.

### Accomplishments we're proud of

Built a working prototype in under 36 hours that feels usable and fun. Created a clean brand identity that captures the “ship it” mindset. Watched people actually use it and say, “wait, this could actually exist.”

### What we learned

How powerful it is when tech aligns with intent. commit reminded us that sometimes the problem isn’t lack of talent but it’s lack of connection. We learned to think like product managers and designers at once: how to simplify, prioritize, and communicate the value of what we’re building.

### What's next

Messaging feature with the person you matched with Sending a message to the person you want to match with Smart recommendations (AI matching by skills/interests) Project tracking features

## README (from the GitHub repository)

# commit :D

https://commit-jade.vercel.app

## Detected evidence (automated analysis)

Indexed codebase: 121 recognized source files, 486 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Next.js (technology) — claimed on Devpost, not found in the code
- AI coding agent: GitHub Copilot — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 133)

```
.gitignore
backend/.gitignore
backend/db/schema.sql
backend/db/seed.sql
backend/package.json
backend/README.md
backend/src/app.js
backend/src/lib/jwt.js
backend/src/lib/supabase.js
backend/src/middleware/auth.js
backend/src/middleware/csrf.js
backend/src/middleware/passwordValidator.js
backend/src/middleware/rateLimiter.js
backend/src/README.md
backend/src/routes/applications.js
backend/src/routes/auth.js
backend/src/routes/csrf.js
backend/src/routes/health.js
backend/src/routes/notifications.js
backend/src/routes/projects.js
backend/src/routes/users.js
backend/src/swipes/controller.js
backend/src/swipes/helpers.js
backend/src/swipes/routes.js
backend/SWIPE_API.md
frontend/.gitignore
frontend/bun.lockb
frontend/components.json
frontend/eslint.config.js
frontend/eslint.config.ts
frontend/index.html
frontend/package.json
frontend/postcss.config.js
frontend/public/robots.txt
frontend/README.md
frontend/src/App.css
frontend/src/App.tsx
frontend/src/components/auth/AccountRequiredDialog.tsx
frontend/src/components/layout/Navbar.tsx
frontend/src/components/layout/ProtectedRoute.tsx
frontend/src/components/match/SwipeCard.tsx
frontend/src/components/profile/MatchCard.tsx
frontend/src/components/profile/MyProjectCard.tsx
frontend/src/components/profile/ProfileCard.tsx
frontend/src/components/profile/ProjectCard.tsx
frontend/src/components/profile/RequestCard.tsx
frontend/src/components/projects/FeedProjectCard.tsx
frontend/src/components/projects/HomeProjectCard.tsx
frontend/src/components/projects/PostProjectDialog.tsx
frontend/src/components/projects/ProjectCard.tsx
frontend/src/components/registration/ProgressSidebar.tsx
frontend/src/components/ui/accordion.tsx
frontend/src/components/ui/alert-dialog.tsx
frontend/src/components/ui/alert.tsx
frontend/src/components/ui/aspect-ratio.tsx
frontend/src/components/ui/avatar.tsx
frontend/src/components/ui/badge.tsx
frontend/src/components/ui/breadcrumb.tsx
frontend/src/components/ui/button.tsx
frontend/src/components/ui/calendar.tsx
frontend/src/components/ui/card.tsx
frontend/src/components/ui/carousel.tsx
frontend/src/components/ui/chart.tsx
frontend/src/components/ui/checkbox.tsx
frontend/src/components/ui/collapsible.tsx
frontend/src/components/ui/command.tsx
frontend/src/components/ui/context-menu.tsx
frontend/src/components/ui/dialog.tsx
frontend/src/components/ui/drawer.tsx
frontend/src/components/ui/dropdown-menu.tsx
frontend/src/components/ui/form.tsx
frontend/src/components/ui/hover-card.tsx
frontend/src/components/ui/input-otp.tsx
frontend/src/components/ui/input.tsx
frontend/src/components/ui/label.tsx
frontend/src/components/ui/menubar.tsx
frontend/src/components/ui/navigation-menu.tsx
frontend/src/components/ui/pagination.tsx
frontend/src/components/ui/popover.tsx
frontend/src/components/ui/progress.tsx
frontend/src/components/ui/radio-group.tsx
frontend/src/components/ui/resizable.tsx
frontend/src/components/ui/scroll-area.tsx
frontend/src/components/ui/select.tsx
frontend/src/components/ui/separator.tsx
frontend/src/components/ui/sheet.tsx
frontend/src/components/ui/sidebar.tsx
frontend/src/components/ui/skeleton.tsx
frontend/src/components/ui/slider.tsx
frontend/src/components/ui/sonner.tsx
frontend/src/components/ui/switch.tsx
frontend/src/components/ui/table.tsx
frontend/src/components/ui/tabs.tsx
frontend/src/components/ui/textarea.tsx
frontend/src/components/ui/toast.tsx
frontend/src/components/ui/toaster.tsx
frontend/src/components/ui/toggle-group.tsx
frontend/src/components/ui/toggle.tsx
frontend/src/components/ui/tooltip.tsx
frontend/src/components/ui/use-toast.ts
frontend/src/contexts/AuthContext.tsx
frontend/src/hooks/use-mobile.tsx
frontend/src/hooks/use-toast.ts
frontend/src/index.css
frontend/src/lib/api.ts
frontend/src/lib/authToken.ts
frontend/src/lib/csrfToken.ts
frontend/src/lib/http.ts
frontend/src/lib/utils.ts
frontend/src/main.tsx
frontend/src/pages/Auth.tsx
frontend/src/pages/AuthCallback.tsx
frontend/src/pages/CreateProject.tsx
frontend/src/pages/Home.tsx
frontend/src/pages/Match.tsx
frontend/src/pages/NotFound.tsx
frontend/src/pages/Profile.tsx
frontend/src/pages/ProjectDetail.tsx
frontend/src/pages/Projects.tsx
frontend/src/pages/Register.tsx
[13 more files omitted for size]
```

### Dependencies

- backend/package.json: @supabase/supabase-js@^2.76.1, cookie-parser@^1.4.7, cors@^2.8.5, dotenv@^17.2.3, express@^5.1.0, express-rate-limit@^8.1.0, helmet@^8.1.0, jsonwebtoken@^9.0.2, nodemon@^3.0.1, zod@^4.2.1
- frontend/package.json: @eslint/js@^9.39.4, @hookform/resolvers@^3.10.0, @radix-ui/react-accordion@^1.2.11, @radix-ui/react-alert-dialog@^1.1.14, @radix-ui/react-aspect-ratio@^1.1.7, @radix-ui/react-avatar@^1.1.10, @radix-ui/react-checkbox@^1.3.2, @radix-ui/react-collapsible@^1.1.11, @radix-ui/react-context-menu@^2.2.15, @radix-ui/react-dialog@^1.1.14, @radix-ui/react-dropdown-menu@^2.1.15, @radix-ui/react-hover-card@^1.1.14, @radix-ui/react-label@^2.1.7, @radix-ui/react-menubar@^1.1.15, @radix-ui/react-navigation-menu@^1.2.13, @radix-ui/react-popover@^1.1.14, @radix-ui/react-progress@^1.1.7, @radix-ui/react-radio-group@^1.3.7, @radix-ui/react-scroll-area@^1.2.9, @radix-ui/react-select@^2.2.5, @radix-ui/react-separator@^1.1.7, @radix-ui/react-slider@^1.3.5, @radix-ui/react-slot@^1.2.3, @radix-ui/react-switch@^1.2.5, @radix-ui/react-tabs@^1.1.12, @radix-ui/react-toast@^1.2.14, @radix-ui/react-toggle@^1.1.9, @radix-ui/react-toggle-group@^1.1.10, @radix-ui/react-tooltip@^1.2.7, @tailwindcss/typography@^0.5.16, @tanstack/react-query@^5.83.0, @types/node@^22.16.5, @types/react@^18.3.23, @types/react-dom@^18.3.7, @vercel/analytics@^1.6.1, @vercel/speed-insights@^1.3.1, @vitejs/plugin-react-swc@^3.11.0, autoprefixer@^10.4.21, axios@^1.7.9, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@^1.1.1, date-fns@^3.6.0, embla-carousel-react@^8.6.0, eslint@^9.39.4, eslint-plugin-react@^7.37.5, eslint-plugin-react-hooks@^5.2.0, eslint-plugin-react-refresh@^0.4.20, globals@^15.15.0, helmet@^8.1.0, input-otp@^1.4.2, jiti@^2.6.1, lovable-tagger@^1.1.11, lucide-react@^0.462.0, next-themes@^0.3.0, postcss@^8.5.6, prettier@^3.8.1, react@^18.3.1, react-day-picker@^8.10.1, react-dom@^18.3.1, react-hook-form@^7.61.1, react-resizable-panels@^2.1.9, react-router-dom@^6.30.1, recharts@^2.15.4, sonner@^1.7.4, tailwind-merge@^2.6.0, tailwindcss@^3.4.17, tailwindcss-animate@^1.0.7, typescript@^5.8.3, typescript-eslint@^8.57.1, vaul@^0.9.9, vite@^5.4.19, zod@^3.25.76

### Recent commits (newest first)

- change landing
- Update README.md
- Jyc/enable csrf (#64)
- toggle csrf (#63)
- turn off toggle
- auth fix (#62)
- check-auth fixed
- fix name (#54)
- Fix swipe (#53)
- add analytics (#52)
- Dev branch (#51)
- Dev branch (#50)
- Dev branch (#48)
- update readme (#46)
- update dev
- Fix/deploy v1 (#45)
- Dev (#44)
- Dev (#43)
- fixed sum stuff (#36)
- rate limit fix?

## Key source files (fetched from GitHub, selected and truncated for size)

### INTEGRATION_GUIDE.md

```markdown
# Frontend-Backend Integration Guide

## Overview
The frontend and backend authentication systems are now fully connected. Users can register, login, and complete their profile through the integrated flow.

## Features Implemented

### ✅ Authentication Integration
- **API Service Layer**: Centralized API calls with proper error handling
- **Authentication Context**: React context for managing user state across the app
- **Login Flow**: Email/password authentication with backend validation
- **Registration Flow**: User account creation with profile completion
- **Profile Creation**: User profile data stored in Supabase via backend API

### ✅ User Experience
- **Loading States**: Visual feedback during API calls
- **Error Handling**: User-friendly error messages
- **Form Validation**: Client-side validation with Zod schemas
- **Responsive Design**: Maintains existing UI/UX design

## How to Run

### Backend (Port 4000)
```bash
cd backend
npm install
npm start
```

### Frontend (Port 5173)
```bash
cd frontend
npm install
npm run dev
```

## API Endpoints Used

### Authentication
- `POST /auth/register` - User registration
- `POST /auth/login` - User login
- `POST /auth/check-password` - Password strength validation

### User Profile
- `POST /users` - Create user profile
- `GET /users/profile` - Get current user profile
- `GET /users/:id` - Get user profile by ID

## Environment Configuration

The frontend automatically connects to `http://localhost:4000` for the backend API. To change this, update the `VITE_API_URL` environment variable in the frontend.

## User Flow

1. **Registration**: User enters email/password → Account created → Redirected to profile setup
2. **Profile Setup**: User completes profile information → Profile saved to database
3. **Login**: User enters credentials → Authenticated → Redirected to home page

## Security Features

- **Password Validation**: Backend validates password strength
- **Rate Limiting**: API calls are rate-limited to prevent abuse
- **Token Management**: JWT tokens stored securely in localStorage
- **Authentication Guards**: Protected routes require authentication

## Next Steps

The authentication system is now fully functional. You can:
1. Test the registration and login flows
2. Add protected routes that require authentication
3. Implement logout functionality
4. Add password reset features
5. Integrate with other parts of your application

```

### backend/SWIPE_API.md

```markdown
# Swipe API Documentation

This document describes the API endpoints for the Tinder-like swiping functionality for projects and users.

## Base URL
```
http://localhost:4000
```

## New API Endpoints (Recommended)

### 1️⃣ User → Project Swiping

#### GET `/swipes/next-project`
Pull a random project the user hasn't swiped on yet.

**Query Parameters:**
- `userId` (required): UUID of the user requesting projects

**Response:**
```json
{
  "id": "uuid",
  "title": "Project Name",
  "description": "Project description",
  "tags": ["react", "nodejs"],
  "looking_for": ["frontend", "backend"],
  "is_active": true,
  "created_at": "2024-01-01T00:00:00Z",
  "owner_id": "uuid",
  "users": {
    "username": "owner_username",
    "role": "backend",
    "experience": "intermediate"
  }
}
```

**No More Projects Response:**
```json
{
  "message": "No more projects available."
}
```

#### POST `/swipes/project`
Record user's swipe action (like or pass) on a project.

**Request Body:**
```json
{
  "project_id": "uuid",
  "direction": "like", // or "pass"
  "userId": "uuid"
}
```

**Response:**
```json
{
  "success": true,
  "swipe": {
    "id": "uuid",
    "swiper_id": "uuid",
    "target_project_id": "uuid",
    "direction": "like",
    "created_at": "2024-01-01T00:00:00Z"
  },
  "message": "Project liked!",
  "match": true, // only present if match found
  "project_id": "uuid" // only present if match found
}
```

### 2️⃣ Project → User Swiping

#### GET `/swipes/next-user`
Pull a random user for project swiping (users who have applied to or liked the project).

**Query Parameters:**
- `projectId` (required): UUID of the project

**Response:**
```json
{
  "id": "uuid",
  "username": "user_username",
  "role": "frontend",
  "experience": "intermediate",
  "time_commitment": "5 hrs/week",
  "tech_tags": ["react", "typescript"]
}
```

**No More Users Response:**
```json
{
  "message": "No more users available."
}
```

#### POST `/swipes/user`
Record project owner's swipe action on a user.

**Request Body:**
```json
{
  "user_id": "uuid",
  "project_id": "uuid",
  "direction": "like" // or "pass"
}
```

**Response:**
```json
{
  "success": true,
  "swipe": {
    "id": "uuid",
    "swiper_id": "uuid", // project owner's ID
    "target_user_id": "uuid",
    "direction": "like",
    "created_at": "2024-01-01T00:00:00Z"
  },
  "message": "User liked!",
  "match": true, // only present if match found
  "user_id": "uuid", // only present if match found
  "project_id": "uuid" // only present if match found
}
```

### 3️⃣ Utility Endpoints

#### GET `/swipes/matches`
List all mutual likes (matches) for the current user.

**Query Parameters:**
- `userId` (required): UUID of the user

**Response:**
```json
{
  "matches": [
    {
      "type": "user_to_project",
      "match_id": "user_id_project_id",
      "user_swipe": {
        "id": "uuid",
        "direction": "like",
        "created_at": "2024-01-01T00:00:00Z"
      },
      "project": {
        "id": "uuid",
      
[truncated — 4808 more characters]
```

### backend/package.json

```
{
  "name": "backend",
  "version": "1.0.0",
  "description": "Backend server for commit project",
  "main": "src/app.js",
  "type": "module",
  "scripts": {
    "start": "node src/app.js",
    "dev": "nodemon src/app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@supabase/supabase-js": "^2.76.1",
    "cookie-parser": "^1.4.7",
    "cors": "^2.8.5",
    "dotenv": "^17.2.3",
    "express": "^5.1.0",
    "express-rate-limit": "^8.1.0",
    "helmet": "^8.1.0",
    "jsonwebtoken": "^9.0.2",
    "zod": "^4.2.1"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}
```

### frontend/package.json

```
{
  "name": "vite_react_shadcn_ts",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "build:dev": "vite build --mode development",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "@hookform/resolvers": "^3.10.0",
    "@radix-ui/react-accordion": "^1.2.11",
    "@radix-ui/react-alert-dialog": "^1.1.14",
    "@radix-ui/react-aspect-ratio": "^1.1.7",
    "@radix-ui/react-avatar": "^1.1.10",
    "@radix-ui/react-checkbox": "^1.3.2",
    "@radix-ui/react-collapsible": "^1.1.11",
    "@radix-ui/react-context-menu": "^2.2.15",
    "@radix-ui/react-dialog": "^1.1.14",
    "@radix-ui/react-dropdown-menu": "^2.1.15",
    "@radix-ui/react-hover-card": "^1.1.14",
    "@radix-ui/react-label": "^2.1.7",
    "@radix-ui/react-menubar": "^1.1.15",
    "@radix-ui/react-navigation-menu": "^1.2.13",
    "@radix-ui/react-popover": "^1.1.14",
    "@radix-ui/react-progress": "^1.1.7",
    "@radix-ui/react-radio-group": "^1.3.7",
    "@radix-ui/react-scroll-area": "^1.2.9",
    "@radix-ui/react-select": "^2.2.5",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slider": "^1.3.5",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-switch": "^1.2.5",
    "@radix-ui/react-tabs": "^1.1.12",
    "@radix-ui/react-toast": "^1.2.14",
    "@radix-ui/react-toggle": "^1.1.9",
    "@radix-ui/react-toggle-group": "^1.1.10",
    "@radix-ui/react-tooltip": "^1.2.7",
    "@tanstack/react-query": "^5.83.0",
    "@vercel/analytics": "^1.6.1",
    "@vercel/speed-insights": "^1.3.1",
    "axios": "^1.7.9",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.1.1",
    "date-fns": "^3.6.0",
    "embla-carousel-react": "^8.6.0",
    "helmet": "^8.1.0",
    "input-otp": "^1.4.2",
    "lucide-react": "^0.462.0",
    "next-themes": "^0.3.0",
    "react": "^18.3.1",
    "react-day-picker": "^8.10.1",
    "react-dom": "^18.3.1",
    "react-hook-form": "^7.61.1",
    "react-resizable-panels": "^2.1.9",
    "react-router-dom": "^6.30.1",
    "recharts": "^2.15.4",
    "sonner": "^1.7.4",
    "tailwind-merge": "^2.6.0",
    "tailwindcss-animate": "^1.0.7",
    "vaul": "^0.9.9",
    "zod": "^3.25.76"
  },
  "devDependencies": {
    "@eslint/js": "^9.39.4",
    "@tailwindcss/typography": "^0.5.16",
    "@types/node": "^22.16.5",
    "@types/react": "^18.3.23",
    "@types/react-dom": "^18.3.7",
    "@vitejs/plugin-react-swc": "^3.11.0",
    "autoprefixer": "^10.4.21",
    "eslint": "^9.39.4",
    "eslint-plugin-react": "^7.37.5",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.20",
    "globals": "^15.15.0",
    "jiti": "^2.6.1",
    "lovable-tagger": "^1.1.11",
    "postcss": "^8.5.6",
    "prettier": "^3.8.1",
    "tailwindcss": "^3.4.17",
    "typescript": "^5.8.3",
    "typescript-eslint": "^8.57.1",
    "vite": "^5.4.19"
  }
}

```

### frontend/src/main.tsx

```typescript
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

createRoot(document.getElementById("root")!).render(
    <QueryClientProvider client={queryClient}>
        <App />
    </QueryClientProvider>
);

```

### frontend/src/App.tsx

```typescript
import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { AuthProvider } from "@/contexts/AuthContext";
import { ProtectedRoute } from "./components/layout/ProtectedRoute";
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/react";

// Pages
import Home from "./pages/Home";
import Projects from "./pages/Projects";
import ProjectDetail from "./pages/ProjectDetail";
import Match from "./pages/Match";
import Profile from "./pages/Profile";
import CreateProject from "./pages/CreateProject";
import Welcome from "./pages/Welcome";
import Auth from "./pages/Auth";
import AuthCallback from "./pages/AuthCallback";
import Register from "./pages/Register";
import RegisterStep1 from "./pages/RegisterStep1";
import RegisterStep2 from "./pages/RegisterStep2";
import RegisterStep3 from "./pages/RegisterStep3";
import NotFound from "./pages/NotFound";

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <AuthProvider>
      <TooltipProvider>
        <Toaster />
        <Sonner />
        <Analytics />
        <SpeedInsights />
        <BrowserRouter>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/home" element={<Home />} />
            <Route path="/welcome" element={<Welcome />} />
            <Route path="/auth" element={<Auth />} />
            <Route path="/auth/callback" element={<AuthCallback />} />
            <Route path="/register" element={<Register />} />
            <Route path="/register/step1" element={<RegisterStep1 />} />
            <Route path="/register/step2" element={<RegisterStep2 />} />
            <Route path="/register/step3" element={<RegisterStep3 />} />
            <Route path="/projects" element={<Projects />} />
            <Route path="/projects/:id" element={<ProjectDetail />} />

            {/* --- PROTECTED ROUTES (Only accessible if logged in) --- */}
            <Route element={<ProtectedRoute />}>
              <Route path="/match" element={<Match />} />
              <Route path="/profile" element={<Profile />} />
              <Route path="/create-project" element={<CreateProject />} />
            </Route>
            <Route path="*" element={<NotFound />} />
          </Routes>
        </BrowserRouter>
      </TooltipProvider>
    </AuthProvider>
  </QueryClientProvider>
);

export default App;

```

### backend/src/app.js

```javascript
import "dotenv/config";
import express from "express";
import cors from "cors";
import helmet from "helmet";
import cookieParser from "cookie-parser";
import authRoutes from "./routes/auth.js";
import userRoutes from "./routes/users.js";
import healthRoutes from "./routes/health.js";
import applicationRoutes from "./routes/applications.js";
import projectRoutes from "./routes/projects.js";
import notificationRoutes from "./routes/notifications.js";
import swipesRoutes from "./swipes/routes.js";
import csrfRoutes from "./routes/csrf.js";
import { generalLimiter, rateLimitStatusRouter } from "./middleware/rateLimiter.js";
import { csrfMiddleware } from "./middleware/csrf.js";

const app = express();

// Trust proxy for rate limiting (required for Render/Vercel)
app.set('trust proxy', 1);

// Security headers
app.use(helmet());

const allowedOrigins = new Set([
    "http://localhost:8080",
    "http://localhost:3000",
    "https://commit-jade.vercel.app",
]);

app.use(
    cors({
        origin: (origin, callback) => {
            if (!origin) return callback(null, true);

            try {
                const url = new URL(origin);
                const hostname = url.hostname;

                if (allowedOrigins.has(origin)) return callback(null, true);
                if (hostname.endsWith(".vercel.app")) return callback(null, true);

                return callback(new Error("Not allowed by CORS"));
            } catch {
                return callback(new Error("Not allowed by CORS"));
            }
        },
        credentials: true,
        methods: ["GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"],
        allowedHeaders: ["Content-Type", "Authorization", "X-CSRF-Token"],
    })
);

app.use(cookieParser());
app.use(express.json());

// Apply general rate limiting to all routes
app.use(generalLimiter);

// ===========================
// ROUTE IMPORTS
// ===========================

// Rate limit status routes
app.use("/rate-limit", rateLimitStatusRouter);

app.get("/", (req, res) => {
    res.redirect("/health");
});

// Health check routes
app.use("/health", healthRoutes);

// CSRF token endpoint (double-submit cookie)
app.use(csrfRoutes);

// Authentication routes
app.use("/auth", authRoutes);

// User profile routes
app.use("/users", csrfMiddleware, userRoutes);

// Projects routes
app.use("/projects", csrfMiddleware, projectRoutes);

// Applications/Requests routes
app.use("/applications", csrfMiddleware, applicationRoutes);

// Notifications routes
app.use("/notifications", csrfMiddleware, notificationRoutes);

// Swipes functionality
app.use("/swipes", csrfMiddleware, swipesRoutes);

// Start the server
app.listen(process.env.PORT || 4000, () =>
    console.log(`running on http://localhost:${process.env.PORT || 4000}`)
);

```

### frontend/postcss.config.js

```javascript
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

```

### frontend/eslint.config.ts

```typescript
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import pluginReact from "eslint-plugin-react";
import { defineConfig } from "eslint/config";

export default defineConfig([
  { files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.browser } },
  tseslint.configs.recommended,
  pluginReact.configs.flat.recommended,
]);

```

### frontend/vite.config.ts

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";
import { componentTagger } from "lovable-tagger";

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
  server: {
    host: "::",
    port: 8080,
  },
  plugins: [react(), mode === "development" && componentTagger()].filter(Boolean),
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  optimizeDeps: {
    force: true,
  },
}));

```

[109 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]