InternHack is a full-stack internship/career platform for students with AI-powered tools, learning modules, and recruiter features.
- Server:
cd server && npm run dev(runstsx watch src/index.ts) - Client:
cd client && npm run dev(Vite on port 5173) - Migrations: Must run from
server/src/database/, that's whereprisma.config.tslives
Always read .Codex/REPO_MAP.md before any editing task. It maps every module, route, model, and component.
- Client: React 18 + Vite 7 + TailwindCSS 4 + React Router 7 + Framer Motion + Zustand + React Query
- Server: Express 5 + TypeScript 5 + Prisma 7 + PostgreSQL
- AI: Google Gemini (
gemini-2.5-flash-lite) - Auth: JWT in
Authorization: Bearer <token>header; Zustand auth store on client - Payments: dodo payment
- Storage: AWS S3 with local fallback
- Use canonical TW v4 classes only
- No
bg-gradient-*shorthand, usebg-linear-to-*instead - No arbitrary bracket sizes like
text-[17px], use standard scale classes - No gradient backgrounds on icons, use flat color or bare colored icons
- No pill badges, do not use
rounded-fullcategory/section tag pills above page headings. Usemt-6on the header wrapper for top spacing instead - Company avatars: first-letter initial in neutral box, not generic icon
- DRY: no duplicate helpers, shared animation variants per file
- Strict mode enabled on both client and server
- Use Zod for all server-side validation (see
*.validation.tsfiles) - Client types mirror server models in
client/src/lib/types.ts
Every server module follows: <name>.routes.ts → <name>.controller.ts → <name>.service.ts
- Routes: Express router with middleware chain (auth, role, usage-limit, validation)
- Controller: Handles request/response, calls service, formats errors
- Service: Business logic, DB queries, external API calls
For premium-only features (see latex-chat.controller.ts):
const user = await prisma.user.findUnique({
where: { id: req.user.id },
select: { subscriptionPlan: true, subscriptionStatus: true },
});
const isPremium =
(user.subscriptionPlan === "MONTHLY" || user.subscriptionPlan === "YEARLY") &&
user.subscriptionStatus === "ACTIVE";Daily usage limits are enforced via usageLimit(action) middleware.
Actions: ATS_SCORE, COVER_LETTER, GENERATE_RESUME, JOB_APPLICATION, MOCK_INTERVIEW
Config in server/src/config/usage-limits.ts.
- Create/update
*.routes.tswith Express router - Create/update
*.controller.tswith request handling - Create/update
*.service.tswith business logic - Create/update
*.validation.tswith Zod schemas - Register routes in
server/src/index.ts
- Create page component in
client/src/module/<area>/<PageName>.tsx - Add lazy import in
client/src/App.tsx - Add route in the appropriate route group (public/student/recruiter/admin)
- Use
GoogleGenerativeAIfrom@google/generative-ai - Model:
gemini-2.5-flash-lite - For structured responses with LaTeX content, use XML tags (
<reply>,<latex>) instead of JSON - Always handle parse failures with fallbacks
Use the reusable Button from client/src/components/ui/button.tsx (CVA-based) for all new buttons.
- Variants:
primary,secondary,mono,ghost,danger - Modes:
button(default),icon,link - Sizes:
sm,md,lg - Supports
asChild(Radix Slot) for composing with<Link>or other elements - Import:
import { Button } from "../../components/ui/button"
All admin and recruiter pages must include <SEO title="Page Title" noIndex />. Public pages use full SEO props.
DynamicFieldRenderer.tsx validates file uploads client-side (size ≤ 5 MB, allowed types). Follow this pattern for any new file upload UI.
Wrap list-rendered child components (cards, badges, list items) with React.memo when they receive stable props and don't have internal state that changes frequently. Use named function form:
export const MyCard = React.memo(function MyCard({ data }: Props) { ... });Students can suggest repos via POST /api/opensource/requests. Admin reviews at /admin/repo-requests. On approval, the repo is auto-created and the student gets an email. Routes are in opensource.routes.ts, /requests/* routes must appear BEFORE /:id to avoid route conflicts.
- Create Prisma migrations without confirming, run from
server/src/database/ - Use
bg-gradient-*in TailwindCSS, usebg-linear-to-* - Add
rounded-fullpill badges above headings - Fabricate new UsageAction enum values, reuse existing ones to avoid migrations
- Push to remote without explicit permission