# Project export: TigerPop

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: TreeHacks 2026
- Tagline: TigerPop is a secure, campus-only marketplace that lets students list, discover, and sell items with image-backed listings, buyer intent tracking, and admin moderation.
- Devpost: https://devpost.com/software/tigerpop
- GitHub: https://github.com/angelztang/treehacks26
- Video: https://www.youtube.com/embed/-_V9TcEoSuE?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

TigerPop began from a common, frustrating campus problem: students constantly need textbooks, furniture, and event tickets, while others are looking to sell or give them away. However, the current solutions—scattered group chats and clunky social media groups—lack safe discovery, simple communication, and streamlined workflows. We set out to build more than just a classifieds page. We wanted a polished marketplace tailored specifically for campus life: fast, privacy-aware, and easy to operate for both students and administrators. Our goal was to create a modern product that lowers the friction of real-world transactions while keeping the community's trust intact.

### What it does

TigerPop is a full-stack campus marketplace that enables verified communities to list, discover, and close sales with minimal friction. The platform manages the entire listing lifecycle—from creation and image management to status transitions (Available, Pending, Sold). Key features include: Dynamic Listing Lifecycle: Comprehensive CRUD operations for listings, allowing users to set categories, conditions, and prices. Smart Image Management: Multi-image uploads backed by Cloudinary with CDN-optimized delivery and server-side validation. Buyer Intent Capture: A "Request to Buy" system that records interest in the database and automatically moves listings to a "Pending" state to prevent double-selling. Dual Dashboards: Specialized views for sellers to manage active listings and for buyers to track purchased items and active requests. Admin Suite: Tools for moderation, category management, and CSV report exports for campus auditing.

### How we built it

We adopted an API-first architecture, utilizing a TypeScript-based React frontend and a Flask (Python) backend. The Architecture Frontend: React + TypeScript with Tailwind CSS for a responsive, modern UI. Backend: Flask organized with modular Blueprints for Authentication, Listings, and User Profiles. Database: PostgreSQL managed via SQLAlchemy ORM. We used Alembic to track and manage schema migrations. Infrastructure: The project is designed for Vercel, utilizing serverless-friendly patterns. For instance, we dynamically adjust our database pooling based on the environment: Infrastructure: The project is designed for Vercel, utilizing serverless-friendly patterns. For instance, we dynamically adjust our database pooling based on the environment: Media: Cloudinary handles image storage and transformations, ensuring our server remains lightweight. Media: Cloudinary handles image storage and transformations, ensuring our server remains lightweight.

### Challenges we ran into

Serverless DB Management: Ensuring short-lived serverless invocations didn't exhaust database connections was a hurdle. We solved this by implementing NullPool settings to ensure connections are closed immediately after a request. Upload Permutations: We spent significant time balancing the security of server-side image processing against the speed and convenience of client-signed Cloudinary uploads. UX vs. Moderation: Designing a flow that allows for "frictionless" posting while building in the hooks for future spam prevention and rate-limiting required careful state management.

### Accomplishments we're proud of

Complete Lifecycle Flow: Successfully implementing a robust "Request-to-Buy" logic that reliably transitions database states. Clean Separation of Concerns: Building a distinct modular backend that is ready to be consumed by a future mobile app without requiring code changes. Production Readiness: Even at the MVP stage, we implemented Alembic migrations and environment-aware configurations, ensuring the app is ready for a real-world campus rollout.

### What we learned

Deployment is a Feature: We learned that production-like considerations—such as database pooling, CDN usage, and CORS-safe configurations—are just as vital as the core business logic. Contract First Development: Investing early in the backend-frontend "contract" (API design) allowed our team to work in parallel without constant blockers. The Power of Serverless: Building with Vercel taught us how to design Python applications that are truly stateless and scalable.

### What's next

Notifications: Integrating SendGrid or Resend to move beyond database "intent" to real-time email/SMS alerts for sellers. Identity Verification: Implementing campus NetID/CAS integration to ensure that only verified students can participate in the marketplace. Rate-Limiting: Adding middleware to protect critical endpoints (uploads and requests) from automated spam. AI Enrichment: Utilizing computer vision to provide condition estimates and price suggestions based on uploaded listing photos. Built With: Backend: Python, Flask, SQLAlchemy, Alembic, PostgreSQL, Cloudinary Frontend: React, TypeScript, Tailwind CSS, React-Router Deployment: Vercel, Python Serverless Functions

## README (from the GitHub repository)

# TigerPop — Princeton community marketplace

TigerPop is a full-stack marketplace web application built for the Princeton community. It lets students buy and sell items (furniture, clothing, textbooks and more), manage listings, favorite items, and communicate interest — all with secure authentication and image uploads.

This repository contains a React + TypeScript frontend (in `frontend/`) and a Flask backend API (in `backend/`). The frontend is deployed as a static site and the backend runs as Python serverless functions (configured for Vercel in `vercel.json`).

## Notable features
- User authentication with CAS integration and JWT session tokens.
- Create, edit, search and filter listings with categories, price and condition.
- Image upload support via Cloudinary (secure image hosting and CDN).
- Listing “hearting” (favorites) and buyer/seller flows including email notifications.
- Responsive modern UI built with React, TypeScript and Tailwind CSS.
- RESTful API built with Flask, SQLAlchemy and PostgreSQL.

## Technology stack
- Frontend: React, TypeScript, Tailwind CSS, react-router.
- Backend: Flask, SQLAlchemy, Flask-Migrate, Flask-JWT-Extended, Flask-Mail.
- Database: PostgreSQL (production), SQLite for local dev.
- Uploads: Cloudinary for image storage and delivery.
- Auth: Princeton CAS integration + JWT for API auth.
- Deployment: Vercel for frontend + serverless Python API; Heroku (historical) examples remain in config.

## Quick start (development)
Prerequisites: Node.js (v18+ recommended), Python 3.10+, and a Postgres DB (or use SQLite locally).

1) Frontend

```bash
cd frontend
npm install
# start dev server
npm run dev
```

2) Backend (local)

```bash
# from repo root
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export DATABASE_URL='sqlite:///local.db'   # or your Postgres DATABASE_URL
export JWT_SECRET_KEY='your-secret'
export SECRET_KEY='your-secret'
python backend/run.py
```

Visit http://localhost:3000 for the frontend and http://localhost:8000 (or configured port) for backend routes.




## Detected evidence (automated analysis)

Indexed codebase: 70 recognized source files, 181 KB.
- CSS (language) — detected in the code
- Flask (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- PostgreSQL (technology) — claimed on Devpost, not found in the code
- Vercel (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (116 of 116)

```
.env.example
.gitignore
.venv311/bin/activate
.venv311/bin/activate.csh
.venv311/bin/activate.fish
.venv311/bin/Activate.ps1
.venv311/bin/alembic
.venv311/bin/dotenv
.venv311/bin/flask
.venv311/bin/gunicorn
.venv311/bin/mako-render
.venv311/bin/normalizer
.venv311/bin/pip
.venv311/bin/pip3
.venv311/bin/pip3.11
.venv311/bin/python
.venv311/bin/python3
.venv311/bin/python3.11
.venv311/bin/wheel
.venv311/pyvenv.cfg
api/index.py
api/requirements.txt
backend/.python-version
backend/app.db
backend/app/__init__.py
backend/app/cas/__init__.py
backend/app/cas/auth.py
backend/app/config.py
backend/app/extensions.py
backend/app/models.py
backend/app/models/__init__.py
backend/app/models/listing.py
backend/app/models/user.py
backend/app/routes.py
backend/app/routes/__init__.py
backend/app/routes/auth_routes.py
backend/app/routes/listing_routes.py
backend/app/routes/routes.py
backend/app/routes/user_routes.py
backend/app/utils/__init__.py
backend/app/utils/assets.py
backend/app/utils/cloudinary_config.py
backend/create_tables.py
backend/drop_columns.py
backend/instance/app.db
backend/instance/tigerpop.db
backend/logs/app.log
backend/manage.py
backend/migrations/alembic.ini
backend/migrations/env.py
backend/migrations/README
backend/migrations/script.py.mako
backend/migrations/versions/20260214_add_netid_to_users.py
backend/migrations/versions/66b831172381_initial_migration.py
backend/Procfile
backend/requirements.txt
backend/run_migrations.py
backend/run.py
backend/test_auth_flow.py
backend/test_upload.py
backend/test_user.py
backend/wsgi.py
deploy.sh
frontend/.gitignore
frontend/config/nginx.conf.erb
frontend/package.json
frontend/postcss.config.js
frontend/Procfile
frontend/public/index.html
frontend/src/App.tsx
frontend/src/components/BuyerDashboard.tsx
frontend/src/components/CreateListing.tsx
frontend/src/components/ErrorBoundary.tsx
frontend/src/components/ListingCard.tsx
frontend/src/components/ListingDetailModal.tsx
frontend/src/components/ListingEditModal.tsx
frontend/src/components/ListingForm.tsx
frontend/src/components/Navbar.tsx
frontend/src/components/PurchaseConfirmationModal.tsx
frontend/src/components/SellerListingModal.tsx
frontend/src/config.ts
frontend/src/index.css
frontend/src/index.tsx
frontend/src/pages/AuthCallback.tsx
frontend/src/pages/BuyerDashboard.tsx
frontend/src/pages/CreateListing.tsx
frontend/src/pages/Dashboard.tsx
frontend/src/pages/ListingDetail.tsx
frontend/src/pages/LoginPage.tsx
frontend/src/pages/MarketplacePage.tsx
frontend/src/pages/SellerDashboard.tsx
frontend/src/pages/SignupPage.tsx
frontend/src/services/api.ts
frontend/src/services/authService.ts
frontend/src/services/listingService.ts
frontend/src/tsconfig.json
frontend/start.sh
frontend/static.json
frontend/tailwind.config.js
frontend/tsconfig.json
local_dev.py
migrations/alembic.ini
migrations/env.py
migrations/README
migrations/script.py.mako
migrations/versions/dd5e442ab72f_add_category_field_to_listing_model.py
package.json
Procfile
README.md
requirements.txt
runtime.txt
scripts/add_user.sql
server.js
SUPABASE_SETUP.md
VERCEL_DEPLOY.md
vercel.json
```

### Dependencies

- api/requirements.txt: bcrypt@==4.1.2, cloudinary@==1.33.0, Flask@==3.0.2, flask-cors@==4.0.0, Flask-JWT-Extended@==4.6.0, Flask-Mail@==0.9.1, Flask-Migrate@==4.0.5, Flask-SQLAlchemy@==3.1.1, Pillow@==10.2.0, psycopg2-binary@==2.9.9, python-dotenv@==1.0.1, requests@==2.31.0, SQLAlchemy@==2.0.16, Werkzeug@==3.0.0
- backend/requirements.txt: bcrypt@==4.1.2, cloudinary@==1.33.0, Flask@==3.0.2, flask-cors@==4.0.0, Flask-JWT-Extended@==4.6.0, Flask-Mail@==0.9.1, Flask-Migrate@==4.0.5, Flask-SQLAlchemy@==3.1.1, gunicorn@==21.2.0, Pillow@==10.2.0, psycopg2-binary@==2.9.9, python-dotenv@==1.0.1, requests@==2.31.0, SQLAlchemy@==2.0.16, Werkzeug@==3.0.0
- frontend/package.json: @heroicons/react@^2.0.18, @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, @types/axios@^0.9.36, @types/jest@^27.5.2, @types/node@^22.14.1, @types/react@^18.2.55, @types/react-dom@^18.2.19, autoprefixer@^10.4.17, axios@^1.8.4, postcss@^8.4.35, react@^18.2.0, react-dom@^18.2.0, react-router-dom@^6.22.1, react-scripts@5.0.1, serve@14.2.1, tailwindcss@^3.4.1, typescript@^4.9.5, web-vitals@^2.1.4
- package.json: @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, @types/jest@^27.5.2, @types/node@^16.18.68, @types/react@^18.2.48, @types/react-dom@^18.2.18, axios@^1.6.7, react@^18.2.0, react-dom@^18.2.0, react-router-dom@^6.22.1, react-scripts@5.0.1, typescript@^4.9.5, web-vitals@^2.1.4
- requirements.txt: bcrypt@==3.2.0, cloudinary@==1.25.0, Flask@==2.2.5, Flask-Cors@==5.0.1, Flask-JWT-Extended@==4.3.1, Flask-Mail@==0.9.1, Flask-Migrate@==3.1.0, Flask-SQLAlchemy@==3.0.2, gunicorn@==20.1.0, Pillow@==9.3.0, psycopg2-binary@==2.9.2, python-dotenv@==0.19.2, requests@==2.28.1, Werkzeug@==2.2.3

### Recent commits (newest first)

- idk
- create listing
- working basic--signup, signin, fetch and create
- Initial commit

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

### SUPABASE_SETUP.md

```markdown
Supabase setup (local)

This repo now supports creating users in Supabase during signup. The backend reads the following environment variables from `.env` (project root) or the environment:

- SUPABASE_URL — your Supabase project URL (e.g. https://xyz.supabase.co)
- SUPABASE_SERVICE_ROLE_KEY — the Supabase service role (secret) key used for Admin API calls
- SUPABASE_PUBLISHABLE_KEY — (optional) the public publishable key

Important security notes
- Keep `SUPABASE_SERVICE_ROLE_KEY` secret. Do not commit `.env` to version control. This repo's `.gitignore` already includes `.env`.
- Rotate the service role key if it is leaked.

How to run locally
1. Ensure `.env` exists at the repo root with the variables set (or export them in your shell):

```bash
# Example (do not paste secret keys into public places)
export SUPABASE_URL="https://<your-project>.supabase.co"
export SUPABASE_SERVICE_ROLE_KEY="sb_secret_..."
export SUPABASE_PUBLISHABLE_KEY="sb_publishable_..."
```

2. Activate your Python virtualenv and start the backend:

```bash
source .venv311/bin/activate
python backend/run.py
```

3. Start the frontend (in a separate terminal):

```bash
cd frontend
npm install
npm run dev
```

4. Test signup via the frontend at `http://localhost:3000/signup` or via curl:

```bash
curl -X POST -H "Content-Type: application/json" -d '{"username":"alice","email":"alice@example.com","password":"hunter2"}' http://127.0.0.1:8000/api/auth/signup
```

Troubleshooting
- If Supabase returns errors, check logs printed by the backend; the route returns 502 with the Supabase response body on failure.
- Ensure `requests` is installed in the backend environment (`pip install -r backend/requirements.txt`).

Next steps
- Optionally persist the Supabase user id in the local `User` model (adds a DB migration).
- Consider moving fully to Supabase Auth and removing local password storage if desired.

```

### VERCEL_DEPLOY.md

```markdown
## Deploying to Vercel (single project for frontend + backend)

This repo contains a React frontend in `frontend/` (already built in `frontend/build/`) and a Flask backend in `backend/`.

What we did
- Added `vercel.json` so Vercel serves static files from `frontend/build` and routes `/api/*` to a WSGI entry that exposes the Flask app.
- Added `api/index.py` which imports `backend/wsgi.py`'s `app` and exposes it as the WSGI callable.
- Added `api/requirements.txt` so Vercel will install the Python dependencies for the Flask app.
- Updated `backend/app/config.py` so SQLAlchemy uses a NullPool in serverless environments (detects `VERCEL=1`), avoiding pool exhaustion.

Quick checklist before deploying
1. Build the frontend locally (or ensure `frontend/build` exists):

```bash
cd frontend
npm install
npm run build
cd ..
```

2. Push all changes to your Git remote (Vercel links to the repo).

3. In the Vercel dashboard for your project, set these Environment Variables (Production):
   - `DATABASE_URL` (your Postgres connection string)
   - `SECRET_KEY`, `JWT_SECRET_KEY`
   - `CLOUDINARY_*` values if you use image uploads
   - `VERCEL=1` (optional but recommended)

4. Migrations: run Alembic/Flask-Migrate against your managed DB before or after deploy. You can run migrations locally (pointing to the same DATABASE_URL) or use a one-off runner in your DB host.

Notes and caveats
- Serverless functions are short-lived; using a managed Postgres provider optimized for serverless (Neon, Supabase) is recommended.
- We set `NullPool` for SQLAlchemy under serverless env. If you use a connection pooler (PgBouncer) or Neon’s recommended settings you can revisit pooling.
- File uploads cannot be persisted to local disk on Vercel: keep Cloudinary or another external storage.

If you want, I can:
- Port individual Flask blueprints into separate serverless endpoints for finer-grained cold-start control.
- Add a GitHub Action to run migrations on deploy.

Local testing (quick MVP simulation)
-----------------------------------
You can run a small local server that serves the built React app from `frontend/build` and runs the Flask API under `/api/*` to simulate the combined Vercel setup.

1. Build frontend (if not built):

```bash
cd frontend
npm install
npm run build
cd ..
```

2. Create a virtualenv and install backend deps (or use your existing env):

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r backend/requirements.txt
```

3. Run the local combined server (script provided as `local_dev.py`):

```bash
python local_dev.py
```

The server runs on http://127.0.0.1:5000 — API routes remain under `/api/*` and the SPA is served from `/`.

This simulates the single-host deployment and is sufficient for MVP verification. It does not emulate Vercel's serverless constraints (connection pooling, cold starts) but confirms routing and static serving work.

```

### requirements.txt

```
Flask==2.2.5
Flask-SQLAlchemy==3.0.2
Flask-Migrate==3.1.0
psycopg2-binary==2.9.2
Flask-JWT-Extended==4.3.1
cloudinary==1.25.0
bcrypt==3.2.0
python-dotenv==0.19.2
Flask-Cors==5.0.1
gunicorn==20.1.0
Werkzeug==2.2.3
Flask-Mail==0.9.1
requests==2.28.1
Pillow==9.3.0

```

### package.json

```
{
  "name": "tigerpop",
  "version": "0.1.0",
  "private": true,
  "description": "TigerPop marketplace application",
  "main": "server.js",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "@types/jest": "^27.5.2",
    "@types/node": "^16.18.68",
    "@types/react": "^18.2.48",
    "@types/react-dom": "^18.2.18",
    "axios": "^1.6.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.22.1",
    "react-scripts": "5.0.1",
    "typescript": "^4.9.5",
    "web-vitals": "^2.1.4"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "engines": {
    "node": "20.x"
  }
}

```

### api/requirements.txt

```
Flask==3.0.2
Flask-SQLAlchemy==3.1.1
Flask-Migrate==4.0.5
Flask-JWT-Extended==4.6.0
Flask-Mail==0.9.1
Werkzeug==3.0.0
SQLAlchemy==2.0.16
python-dotenv==1.0.1
psycopg2-binary==2.9.9
requests==2.31.0
bcrypt==4.1.2
cloudinary==1.33.0
Pillow==10.2.0
flask-cors==4.0.0

```

### backend/requirements.txt

```
Flask==3.0.2
Flask-SQLAlchemy==3.1.1
Flask-Migrate==4.0.5
Flask-JWT-Extended==4.6.0
Flask-Mail==0.9.1
Werkzeug==3.0.0
SQLAlchemy==2.0.16
python-dotenv==1.0.1
psycopg2-binary==2.9.9
gunicorn==21.2.0
requests==2.31.0
bcrypt==4.1.2
cloudinary==1.33.0
Pillow==10.2.0
flask-cors==4.0.0 
```

### frontend/package.json

```
{
  "name": "tigerpop-frontend",
  "version": "0.1.0",
  "private": true,
  "engines": {
    "node": "20.x"
  },
  "dependencies": {
    "@heroicons/react": "^2.0.18",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "@types/jest": "^27.5.2",
    "@types/react": "^18.2.55",
    "@types/react-dom": "^18.2.19",
    "autoprefixer": "^10.4.17",
    "axios": "^1.8.4",
    "postcss": "^8.4.35",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.22.1",
    "react-scripts": "5.0.1",
    "serve": "14.2.1",
    "tailwindcss": "^3.4.1",
    "typescript": "^4.9.5",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "dev": "react-scripts start",
    "start": "serve -s build --single",
    "build": "CI=false GENERATE_SOURCEMAP=false react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "heroku-postbuild": "npm install && npm run build",
    "heroku-prebuild": "npm install -g serve@14.2.1"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@types/axios": "^0.9.36",
    "@types/node": "^22.14.1"
  }
}

```

### server.js

```javascript
const express = require('express');
const path = require('path');
const app = express();

// Serve static files from the React app
app.use(express.static(path.join(__dirname, 'build')));

// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
}); 
```

### api/index.py

```python
"""
WSGI entry for Vercel serverless Python builder.

This file imports the existing Flask `app` (created in `backend/wsgi.py`) and
exposes it as the WSGI `app` variable. Vercel's Python builder will use this
callable to serve requests under `/api/*`.

Notes:
- Ensure environment variables are configured in the Vercel dashboard.
- The Flask app expects `DATABASE_URL` (Postgres) and other secrets.
"""

from backend.wsgi import app

# Expose the Flask WSGI app as the module-level name expected by many WSGI
# runners. Vercel will mount this and forward HTTP requests to it.
__all__ = ("app",)

```

### frontend/src/index.tsx

```typescript
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

root.render(
  <React.StrictMode>
    <Router>
      <App />
    </Router>
  </React.StrictMode>
); 
```

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