- Create proxy.ts at repo root with clerkMiddleware + createRouteMatcher (public allowlist: sign-up, log-in, forgot-password, reset-password, verify-email) - Create prisma/schema.prisma: Owner, Species, Breed, Pet models with ownership index and FK constraints - Create prisma.config.ts with Prisma 7 defineConfig (datasource url via env) - Create src/lib/prisma.ts: Prisma 7 singleton using @prisma/adapter-pg for local dev, withAccelerate() for Prisma Accelerate on Vercel - Create src/lib/r2.ts (server-only): S3Client + getPresignedUploadUrl with contentType enforcement and 300s expiry - Create src/lib/auth.ts: getOwnerId() Clerk auth wrapper - Create src/trpc/init.ts: createTRPCContext, router, publicProcedure, protectedProcedure (throws UNAUTHORIZED) - Create src/trpc/routers/pets.ts: pets.create (upserts Owner, creates Pet with ownerId=ctx.userId), pets.list (scoped), pets.byId (ownership-asserted) - Create src/trpc/routers/_app.ts: appRouter + AppRouter type export - Create src/trpc/query-client.ts, server.ts (RSC caller), client.tsx (TRPCReactProvider + useTRPC) - Create src/app/api/trpc/[trpc]/route.ts: tRPC fetch handler (GET + POST) - Create src/context/ActivePetContext.tsx + src/hooks/useActivePet.ts: localStorage-backed active pet context - Create prisma/seed.ts: seeds Dog/Cat/Bird species idempotently via upsert - Update src/app/layout.tsx: wrap children in ClerkProvider (orange primary), TRPCReactProvider, ActivePetProvider, Toaster - Install @prisma/adapter-pg + pg for Prisma 7 local dev (Rule 2: required for Prisma 7 engine compatibility) - [Rule 1 - Bug] Prisma 7 no longer supports url/directUrl in schema.prisma; moved to prisma.config.ts with defineConfig
47 lines
1.2 KiB
TypeScript
47 lines
1.2 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { Inter } from "next/font/google";
|
|
import { ClerkProvider } from "@clerk/nextjs";
|
|
import { TRPCReactProvider } from "@/trpc/client";
|
|
import { ActivePetProvider } from "@/context/ActivePetContext";
|
|
import { Toaster } from "@/components/ui/sonner";
|
|
import "./globals.css";
|
|
|
|
const inter = Inter({
|
|
variable: "--font-sans",
|
|
subsets: ["latin"],
|
|
});
|
|
|
|
export const metadata: Metadata = {
|
|
title: "OnlyPets",
|
|
description: "The social platform where pets are the stars.",
|
|
};
|
|
|
|
export default function RootLayout({
|
|
children,
|
|
}: Readonly<{
|
|
children: React.ReactNode;
|
|
}>) {
|
|
return (
|
|
<ClerkProvider
|
|
appearance={{
|
|
variables: {
|
|
colorPrimary: "#F97316",
|
|
colorBackground: "#FAFAFA",
|
|
colorText: "#09090B",
|
|
},
|
|
}}
|
|
>
|
|
<html lang="en" className={`${inter.variable} h-full antialiased font-sans`}>
|
|
<body className="min-h-full flex flex-col">
|
|
<TRPCReactProvider>
|
|
<ActivePetProvider>
|
|
{children}
|
|
<Toaster />
|
|
</ActivePetProvider>
|
|
</TRPCReactProvider>
|
|
</body>
|
|
</html>
|
|
</ClerkProvider>
|
|
);
|
|
}
|