Imported from arunavtiwari/contcave (
AGENTS.md). Install upstream withnpx skills add arunavtiwari/contcave. Copyright stays with the author.
AGENTS.md
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
What this project is
Contcave is a studio booking marketplace. The platform enables hosts to list and manage creative studios (photography, video, podcast, events) with availability and pricing controls, while allowing guests to discover and book these spaces.
Commands
npm run dev # Start dev server
npm run build # Production build
npm run type-check # tsc --noEmit (run this before committing)
npm run check # type-check + lint together
npm run lint:fix # Auto-fix lint issues
npm run test:e2e # Playwright end-to-end tests
npx prisma studio # Open Prisma database GUI
npx prisma generate # Regenerate client after schema changes
Bash commands
Prefer these over defaults when available. Fall back silently if missing.
- Search content:
rgovergrep - Find files:
fdoverfind - Structural/AST search:
ast-grep(sg) for refactors and pattern-based code search in TS/TSX - JSON:
jqfor parsing, filtering, or transformation in pipelines - GitHub operations:
ghfor PRs, issues, reviews, CI status, releases - Circular deps:
madge --circular - Dead code:
knip - Typecheck only:
tsc --noEmit
Route structure
app/
(main)/
(public)/ # Unauthenticated pages: /, /home, /listings/[listingId], /about, /blog
dashboard/ # Authenticated user pages: /bookings, /chat, /properties, /reservations, /profile, /payments
(admin)/ # Admin panel at /admin/dashboard
actions/ # Server Actions (getListings, getCurrentUser, reservationActions, etc.)
api/ # API Routes for webhooks, Cashfree payments, uploads, Ably auth
Middleware in auth.config.ts enforces route protection. PROTECTED_ROUTES requires auth; ADMIN_ROUTES requires role === ADMIN.
Server Action pattern
All mutations use the createAction wrapper from lib/actions-utils.ts:
export const myAction = createAction(
zodSchema,
{ requireAuth: true, allowedRoles: ["OWNER"] },
async (data, { user }) => { /* handler */ }
);
Returns ActionResponse<T> = { success, data?, error?, details? }. Throw UserFacingError for messages intended to reach the client. Other errors are logged and returned as generic 500s in production.
API route pattern
All routes use helpers from lib/api-utils.ts:
return createSuccessResponse(data); // { success: true, data, timestamp }
return createErrorResponse("message", 400); // { success: false, error, timestamp }
return handleRouteError(error, "context"); // logs + returns 500
Data types
Prisma models are never passed to client components directly. Use the "safe" variants which serialize Date → string:
SafeUser— fromtypes/user.ts; includesrole: UserRolesafeListing— fromtypes/listing.ts; omitsaddons,packages,operationalDays/Hours,actualLocationSafeReservation— fromtypes/reservation.ts
Three roles: CUSTOMER | OWNER | ADMIN (defined in both Prisma enum and types/user.ts).
Encryption
Sensitive fields (bank account number, IFSC, GSTIN, Cashfree vendor ID) are stored AES-256-CBC encrypted. Each encrypted value has a companion *IV field storing the initialization vector. Always use encryptionService from lib/security/encryption.ts (server-only) and decryptPaymentDetailsInternal from lib/payment-details.ts to read them — never access the raw DB fields directly.
Payments (Cashfree)
All Cashfree API functions live in lib/cashfree/cashfree.ts:
cfCreateOrder— payment orders (PG API)cfEnsureVendor/cfUpdateVendor— Easy Split vendor managementcfOnDemandTransfer— manual payouts to vendorscfCreateRefund/cfFetchOrder— order management
CASHFREE_ENV=SANDBOX|PRODUCTION controls which base URL is used. All Cashfree HTTP calls must go through the Fixie proxy (getFixieProxyAgent() from lib/fixie-proxy.ts) because Cashfree enforces IP whitelisting. Do not call Cashfree directly without the proxy agent.
CASHFREE_VENDOR_SCHEDULE_OPTION (default 2 = T+1) sets the settlement schedule for new vendors — check which schedule IDs are enabled in the merchant dashboard before changing this.
Storage
Images/files are stored in Cloudflare R2, accessed via the AWS S3 SDK (lib/storage/r2.ts). Presigned URLs are generated server-side for uploads. Public CDN is at assets.contcave.com.
Listing cards
The listing card is composed from three sub-components in components/listing/:
ListingCardMedia— image slideshow, price pill, heart button, reservation status badge, verified badge (top-left pill)ListingCardContent— location label, studio name (line-clamp-2), chips (sq ft, pax, rating)ListingCardActions— host/admin action buttons
ListingCardData in ListingCard.tsx is the shared interface; safeListing satisfies it directly so no mapping is needed in ListingFeed.
Key external services
| Service | Purpose | Config env vars |
|---|---|---|
| Cashfree | Payments + Easy Split payouts | CASHFREE_APP_ID, CASHFREE_SECRET_KEY, CASHFREE_ENV |
| Fixie | Outbound proxy for Cashfree IP whitelist | FIXIE_URL |
| Cloudflare R2 | File storage | CLOUDFLARE_R2_* |
| Ably | Real-time chat | ABLY_CHAT_API |
| MailerSend | Transactional email | MAILERSEND_API_KEY |
| Google OAuth + Calendar | Auth + host calendar sync | GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
| MongoDB Atlas | Database | DATABASE_URL |
Things to know
lib/prismadb.tsisserver-onlyand uses a global singleton to survive hot reloads in dev. Never import it from client components.jsdomandisomorphic-dompurifyare inserverExternalPackages(next.config.ts) to avoid an ESM/CJS conflict at runtime — do not remove this.Calendar.tsx(FullCalendar) is only used in the host dashboard'sSyncCalendarTab; it is not in the public listing page bundle.- Secondary data fetches on
/listings/[listingId](reservations, user, reviews) already run inPromise.all— maintain this pattern when adding more. - The
ListingHeaddesktop gallery shows 5 images above the fold; all 5<Image>components havepriorityset.
This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ (resolved from this file's directory; in monorepos the next package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by next dev — verify at node_modules/next/dist/server/lib/generate-agent-files.js. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.