Skip to content

PixelHalide/clubIndex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ClubIndex

A full-stack web application that centralises club recruitment drives and event announcements for college campuses. Instead of relying on scattered WhatsApp groups where important deadlines get buried, ClubIndex gives every club a verified space to post recruitments and events and gives students a single feed to browse, search, and apply.


Features

For Students

  • Browse Recruitments — paginated feed of open recruitment posts with filters by audience (WC / MC / Other), club type, and free-text search.
  • Browse Events — same experience for campus events (Juniors / Seniors / Other).
  • One-Click Apply — each post links directly to the club's application form; clicks are tracked for analytics.

For Club Admins

  • Post Management — create, edit, and delete recruitment & event posts for your club.
  • Club Profile — update club name, photo, and type.
  • Click Analytics — per-post click charts showing engagement over time.
  • Signup Tokens — administrators generate one-time tokens that let new club admins register.

For Platform Administrators

  • Full Control — create, edit, and delete any club; moderate all posts.
  • Token Management — generate and revoke signup tokens for any club.

Tech Stack

Layer Technology
Frontend Next.js 16, React 19, TypeScript, Tailwind CSS 4, Turbopack
Backend Express 5, TypeScript, Node.js
Database PostgreSQL (via pg)
Auth JWT access + refresh tokens, bcrypt password hashing, HTTP-only cookies

Project Structure

clubIndex/
├── backend/                    # Express API server
│   ├── src/
│   │   ├── index.ts            # Server entry point
│   │   ├── app.ts              # Express app setup & route mounting
│   │   ├── controllers/
│   │   │   ├── auth/           # Login, token refresh, sign-out logic
│   │   │   ├── events/         # Event CRUD controller
│   │   │   ├── recruitments/   # Recruitment CRUD controller
│   │   │   ├── services/       # Shared service helpers
│   │   │   ├── clubController.ts
│   │   │   ├── clickController.ts
│   │   │   └── signUpTokenController.ts
│   │   ├── routes/
│   │   │   ├── auth/           # login, refresh, signout routes
│   │   │   ├── club.routes.ts
│   │   │   ├── event.routes.ts
│   │   │   ├── recruitment.routes.ts
│   │   │   ├── click.routes.ts
│   │   │   └── signUpToken.routes.ts
│   │   ├── middleware/
│   │   │   ├── auth.ts         # JWT verification middleware
│   │   │   └── clickLimiter.ts # Rate-limits clicks (1 per post/IP/day)
│   │   ├── db/
│   │   │   ├── db.ts           # PostgreSQL connection pool
│   │   │   ├── schema.sql      # Table definitions
│   │   │   └── setup.sql       # Full DB reset + seed script
│   │   ├── types/              # TypeScript type declarations
│   │   └── lib/                # Shared utilities
│   ├── .env.example
│   ├── nodemon.json
│   ├── package.json
│   └── tsconfig.json
│
├── club-index/                 # Next.js frontend
│   ├── app/
│   │   ├── page.tsx            # Landing page
│   │   ├── layout.tsx          # Root layout (dark theme, Geist fonts)
│   │   ├── globals.css
│   │   ├── recruitments/       # /recruitments — paginated recruitment feed
│   │   ├── events/             # /events — paginated event feed
│   │   └── manage/
│   │       ├── page.tsx        # Admin dashboard (club admin / administrator)
│   │       └── analytics/      # /manage/analytics/[type]/[postId] — click charts
│   ├── components/
│   │   ├── cards/              # PostCard, EventCard, RecruitmentCard, ClubCard
│   │   ├── forms/              # AuthModal, ClubForm, EventForm, RecruitmentForm
│   │   ├── layout/             # Header, Footer, Panels, Tabs, Pagination, etc.
│   │   └── ui/                 # Badge, Button, Input, Select, TextArea
│   ├── contexts/
│   │   ├── AuthContext.tsx     # Auth state + JWT refresh logic
│   │   └── Providers.tsx
│   ├── lib/
│   │   ├── api.ts              # Typed API client with auto-refresh on 401
│   │   ├── hooks/              # usePaginatedData, etc.
│   │   ├── richText.tsx
│   │   └── utils.ts
│   ├── types/
│   │   └── api.ts              # Shared frontend type definitions
│   ├── public/                 # Static assets
│   ├── package.json
│   └── tsconfig.json
│
├── .gitignore
└── README.md

Database Schema

The PostgreSQL database uses the following tables:

Table Purpose
clubs Club profiles (name, photo, type)
recruitment_posts Recruitment ads with deadline, form link, tags, and audience (WC/MC/Other)
event_posts Event announcements with similar metadata
club_admins Club-scoped admin accounts (bcrypt-hashed passwords)
administrators Platform-wide super-admin accounts
club_signup_tokens One-time tokens for registering new club admin accounts
refresh_tokens JWT refresh tokens for persistent sessions
recruitment_clicks Click tracking for recruitment posts
event_clicks Click tracking for event posts

Notable constraints:

  • Each club can have at most one active MC recruitment post and at most one active WC recruitment post (enforced via partial unique indexes).
  • Tags are stored as JSONB with a GIN index for fast filtering.

API Routes

All routes are prefixed with /api.

Method Route Auth Description
POST /login/login Authenticate (returns access token + refresh cookie)
PUT /login/login Register a new club admin (requires signup token)
POST /refresh/refresh Cookie Refresh the access token
POST /signout/signout JWT Invalidate refresh token
GET /recruitment/recruitment/:page Paginated recruitments (supports query filters)
POST /recruitment/recruitment JWT Create recruitment post
PUT /recruitment/recruitment/:id JWT Update recruitment post
DELETE /recruitment/recruitment/:id JWT Delete recruitment post
PUT /recruitment/recruitment/:id/click Record a click (rate-limited)
GET /recruitment/recruitment/:id/clicks JWT Get click history
GET /event/events/:page Paginated events (supports query filters)
POST /event/events JWT Create event post
PUT /event/events/:id JWT Update event post
DELETE /event/events/:id JWT Delete event post
PUT /event/events/:id/click Record a click (rate-limited)
GET /event/events/:id/clicks JWT Get click history
GET /club/club/list List all clubs
GET /club/club JWT Get current admin's club
POST /club/club JWT Create a club (admin only)
PUT /club/club/:id JWT Update club
DELETE /club/club/:id JWT Delete club
PUT /signup-token/signup-token/:club_id JWT Generate signup token
DELETE /signup-token/signup-token/:token_id JWT Revoke signup token

Getting Started

Prerequisites

  • Node.js ≥ 18
  • PostgreSQL ≥ 14

1. Clone the repo

git clone https://github.com/<your-username>/clubIndex.git
cd clubIndex

2. Set up the database

# Connect to psql and create a database
psql -U postgres -c "CREATE DATABASE clubindex;"

# Run the setup script (creates tables + seeds a default admin)
psql -U postgres -d clubindex -f backend/src/db/setup.sql

3. Configure the backend

cd backend
cp .env.example .env
# Edit .env with your DB credentials and a strong JWT_SECRET
npm install

4. Configure the frontend

cd club-index
npm install

Create a .env.local file:

NEXT_PUBLIC_API_URL=http://localhost:3000

5. Run in development

In two separate terminals:

# Terminal 1 — Backend
cd backend
npm run dev          # starts nodemon with ts-node on port 3000

# Terminal 2 — Frontend
cd club-index
npm run dev          # starts Next.js with Turbopack on port 3001

Environment Variables

Backend (backend/.env)

Variable Description Default
PORT Server port 3000
NODE_ENV Environment development
JWT_SECRET Secret for signing JWTs
DB_USER PostgreSQL user postgres
DB_HOST PostgreSQL host localhost
DB_NAME Database name
DB_PASSWORD Database password
DB_PORT PostgreSQL port 5432

Frontend (club-index/.env.local)

Variable Description Default
NEXT_PUBLIC_API_URL Backend API base URL http://localhost:3000

License

This project is licensed under the GNU General Public License v3.0.

About

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages