The official website of Innovision 2026, the technical fest of NIT Rourkela (13–15 November 2026).
The site takes visitors through four "worlds" of events, with a scroll-driven home page, an event explorer, a 3D photo gallery, a schedule, a merch store and Google sign-in for fest registration. Staff manage it from an admin dashboard built into the site.
Built by OpenCode NIT Rourkela and DSC NIT Rourkela.
- Tech stack
- Getting started
- Environment variables
- Database setup
- Project structure
- How the site works
- Common tasks
- Admin dashboard and roles
- API routes
- Security
- Scripts, CI and deployment
- Contributing
- Contact
| Area | What we use |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) · React 19 · TypeScript |
| Animation | GSAP 3 with ScrollTrigger and ScrambleText |
| Auth and data | Supabase: Google OAuth, Postgres with Row Level Security, Storage |
| Images | ImageKit: event posters and gallery photos, resized on the CDN |
| Hosting | Vercel |
| Tooling | ESLint (eslint-config-next), Husky and lint-staged pre-commit hook, GitHub Actions CI |
Heads-up for contributors: Next.js 16 has breaking changes compared to older versions. For example, middleware is now
src/proxy.ts. Before relying on an API you remember, read the guides bundled innode_modules/next/dist/docs/(seeAGENTS.md).
Requirements: Node.js 22 (the version CI uses) and npm.
# 1. Clone and install
git clone https://github.com/OpencodeNIT-R/project-spacex.git
cd project-spacex
npm install # also installs the Husky pre-commit hook
# 2. Configure the environment
cp .env.example .env # then fill in the values (see below)
# 3. Run the dev server
npm run dev # http://localhost:3000The site renders without Supabase or ImageKit keys, but sign-in, registration, the admin dashboard, and events or gallery photos from the database won't work until they are set.
.env.example is the documented template: copy it to .env (or .env.local). Real env files are
git-ignored, so never commit secrets. Anything prefixed NEXT_PUBLIC_ is bundled into the browser and is public.
| Variable | Required | Purpose |
|---|---|---|
IMAGEKIT_URL_ENDPOINT |
yes | https://ik.imagekit.io/<id>. Also allowed in the Content-Security-Policy img-src. |
IMAGEKIT_PUBLIC_KEY |
yes | Server-side ImageKit SDK. |
IMAGEKIT_PRIVATE_KEY |
yes (secret) | Uploads and deletes posters and gallery images. Server only. |
NEXT_PUBLIC_SUPABASE_URL |
yes | Supabase project URL. |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY |
yes | Publishable (anon) key. Safe in the browser because RLS is enforced. |
SUPABASE_SERVICE_ROLE_KEY |
prod (secret) | Backend API routes. Bypasses RLS and signs staff links to payment proofs. Server only. |
NEXT_PUBLIC_SITE_URL |
optional | Canonical origin for SEO, sitemap and the sign-in return URL. Defaults to https://www.innonitrkl.com. |
GOOGLE_SITE_VERIFICATION |
optional | Google Search Console verification token. |
DATABASE_URL |
local tools only | Direct Postgres connection for running SQL from your machine. Never add it to Vercel. |
The whole schema (tables, Row Level Security policies, triggers, helper functions and the private storage bucket)
is in supabase/schema.sql. It is idempotent, so it is safe to run again after updates.
psql "$DATABASE_URL" -f supabase/schema.sql
# or paste it into Supabase Dashboard → SQL Editor| Table | Holds |
|---|---|
profiles |
One row per signed-in user (created by a trigger on Google sign-up) and their role. |
registrations |
Fest registrations with payment status. Created only through /api/register. |
events |
Events added from the admin dashboard, shown in the event worlds. |
gallery |
Photos uploaded from the admin dashboard. |
user_sessions |
Server-side session records. |
Payment screenshots go to the private Supabase Storage bucket payment-proofs. Staff see them only through
short-lived signed links.
Also configure, in the Supabase dashboard:
- Authentication → Providers → Google: enable Google sign-in.
- Authentication → URL Configuration: add
http://localhost:3000and the production domain.
Scripts in supabase/manual/ are one-off migrations. They are kept out of any automatic migration path on purpose,
so read each file's header before running it.
src/
├── app/ # Next.js App Router
│ ├── page.tsx # The whole interactive site (renders <Innovision />)
│ ├── events/ # Server-rendered event pages, one per world (for SEO and shareable links)
│ ├── register/ merch/ # "Opening soon" pages while those flows are switched off
│ ├── api/ # Route handlers (see "API routes")
│ ├── layout.tsx # Fonts, metadata, JSON-LD
│ ├── sitemap.ts robots.ts # Generated from src/config/site.ts
│ └── opengraph-image.tsx … # Generated social share cards
├── components/
│ ├── innovision/ # The main experience
│ │ ├── Innovision.tsx # The app shell: hash router, GSAP engine, auth, data fetching
│ │ ├── data.ts # Content: worlds, schedule, products, sponsors, gallery, contacts
│ │ ├── gallery.json # Static gallery photo list (ImageKit URLs)
│ │ ├── HomeView.tsx # Landing page
│ │ ├── WorldsView.tsx DetailView.tsx # Event worlds and their detail scenes
│ │ ├── GalleryView.tsx # 3D "fly through" gallery
│ │ ├── ScheduleView.tsx MerchView.tsx
│ │ ├── SiteFooter.tsx Hud.tsx MenuOverlay.tsx …
│ │ ├── AdminDashboard.tsx admin/ # Staff dashboard
│ │ └── AuthOverlay.tsx ProfileOverlay.tsx
│ ├── coming-soon/ # Registration and merch teaser pages
│ └── events/ # Chrome for the /events pages
├── config/
│ ├── launch.ts # Launch switches (registrations, merch store)
│ ├── payment.ts # Registration fee, UPI ID and UPI payment link
│ └── site.ts # Canonical URL and site name
├── lib/ # Server and shared helpers: supabase, imagekit, auth, validation, security, seo
└── proxy.ts # Request proxy (Next 16's middleware): CSRF, rate limits, auth gate
public/assets/ # Illustrations, planets, sounds (WebP and SVG)
supabase/ # schema.sql and manual migrations
The home page is a single-page experience. Innovision.tsx owns the state
and a small hash router, and swaps between views with a curtain transition:
| Route | View |
|---|---|
#/ |
Home |
#/worlds/<slug> |
Event worlds slider |
#/world/<slug> |
A world's detail scene and its events |
#/gallery |
3D gallery |
#/schedule |
Schedule |
#/merch |
Merch store (when open) |
The four worlds and their slugs are flagship-events, standout-events, main-events and dts-and-fun-events.
They also have real server-rendered pages at /events/<slug>, which search engines index and people can share.
Views load lazily on first visit. Heavy animation loops pause while a view is hidden, and low-powered devices (or prefers-reduced-motion) get a lighter mode.
Flip the switches in src/config/launch.ts:
export const REGISTRATIONS_OPEN = true;
export const MERCH_STORE_OPEN = true;While a switch is false, its buttons and links lead to a "coming soon" page, and the real flow stays untouched.
The registration APIs (/api/register, /api/upload) check the same switch on the server.
Edit src/config/payment.ts. The fee must match REGISTRATION_FEE_INR in
src/lib/security.ts, which is what the server records. Then regenerate
public/assets/upi-qr.svg from the new UPI_PAY_LINK as a single byte-mode segment (see the comment in
payment.ts), and scan it to check before deploying. Until the organisers send the real QR, upi-qr.svg is a
"coming soon" placeholder.
Gallery photos are a static list in
src/components/innovision/gallery.json, so the home page needs no
database query to show them.
-
Convert the photo to WebP (around 2000px on the long side and quality 80 is a good default).
-
Upload it to ImageKit in the
/innovision/gallery/archivefolder. -
Add an entry to
gallery.json:{ "id": "innovision-27", "title": "Robo race", "url": "https://ik.imagekit.io/<id>/innovision/gallery/archive/innovision-27.webp", "width": 2000, "height": 1333 }
The first six entries fly through the home page's gallery section. The gallery page (#/gallery) shows all of
them. Pages request resized copies from ImageKit (?tr=w-…), and ImageKit picks WebP or AVIF for each browser.
Photos uploaded from the admin dashboard's Gallery tab are stored in the database and appear on the gallery page after the static list.
Most copy and content lives in src/components/innovision/data.ts: the worlds,
schedule, products, sponsors (TITLE_SPONSOR, SPONSOR_TIERS), navigation links and the footer's CONTACTS.
Events added in the admin dashboard are merged into the worlds at runtime.
Set NEXT_PUBLIC_SITE_URL in Vercel, add the domain under Supabase → Authentication → URL Configuration, then
redeploy. Canonical URLs, Open Graph tags, robots.txt, sitemap.xml and JSON-LD all follow it.
Staff open the dashboard from their profile on the site. Access depends on profiles.role:
| Role | Can do |
|---|---|
user |
Register for the fest and view their pass. |
registration-team |
Review registrations and payment proofs, approve or reject them. |
it-team |
Manage events and gallery photos. |
admin |
Everything above, plus managing users' roles. |
Admins can switch users between user, it-team and registration-team. The admin role itself can only be granted
directly in the database:
update public.profiles set role = 'admin' where email = '[email protected]';| Route | Methods | Access |
|---|---|---|
/api/events |
GET | Public (cached) |
/api/gallery |
GET | Public (cached) |
/api/auth/session |
GET, POST | Session cookie handling |
/api/auth/profile |
POST, PATCH | Signed-in user |
/api/auth/logout |
POST | Signed-in user |
/api/register |
GET, POST | Signed-in user |
/api/upload |
POST | Signed-in user (payment proof) |
/api/admin/registrations |
GET, PATCH | Admin, registration team |
/api/admin/registrations/payment-proof |
GET | Admin, registration team |
/api/admin/events |
GET, POST, PATCH, DELETE | Admin, IT team |
/api/admin/gallery |
GET, POST, PATCH, DELETE | Admin, IT team |
/api/admin/users |
GET, PATCH | Admin |
- Row Level Security on every table. Privileged columns (
role,email, and others) are protected by triggers, and registrations can only be created by the server. src/proxy.tsrejects cross-site writes to/api(CSRF), caps request body size, and rate-limits writes and auth endpoints per IP.- Every route handler checks the Supabase session and the caller's role itself. The proxy's auth gate is only an early filter.
- Strict security headers and a Content-Security-Policy are set in
next.config.ts. - Secrets stay on the server. Never put a secret in a
NEXT_PUBLIC_*variable.
npm run dev # development server (Turbopack)
npm run build # production build
npm run start # serve the production build
npm run lint # ESLint- Pre-commit: Husky runs
eslint --fixon staged JS/TS files. - CI (
.github/workflows/ci.yml): on pushes and PRs tomain, it runsnpm ci,npm run lintandnpm run buildon Node 22. - Deployment: Vercel builds production from the
developmentbranch of OpencodeNIT-R/project-spacex. Set the environment variables above in the Vercel project settings.
- Fork the repo and create a branch from
development. - Keep to the existing code style: content in
data.ts, views as plain components that render from thevvalues computed inInnovision.tsx, and animation in the GSAP engine. - Run
npm run lintandnpm run buildbefore opening a PR. - Open a pull request against
developmenton OpencodeNIT-R/project-spacex.
For queries about the fest:
- Rishav Bansal: +91 79788 88164
- Manjeet Srichandan: +91 77355 10636
Follow Innovision on Instagram and LinkedIn.
Crafted with ♥ by OpenCode NIT Rourkela and DSC NIT Rourkela