├── .nvmrc ├── apps └── web │ ├── src │ ├── lib │ │ ├── utils.ts │ │ ├── fonts.ts │ │ └── validateServerAction.ts │ ├── features │ │ ├── profile │ │ │ ├── index.ts │ │ │ └── components │ │ │ │ └── auth-showcase.tsx │ │ ├── ui │ │ │ ├── index.ts │ │ │ └── components │ │ │ │ ├── theme-toggle.tsx │ │ │ │ └── icons.tsx │ │ └── posts │ │ │ ├── index.ts │ │ │ └── components │ │ │ ├── post-card.tsx │ │ │ └── create-post-form.tsx │ ├── app │ │ ├── (routes) │ │ │ ├── loading.tsx │ │ │ ├── error.tsx │ │ │ ├── page.tsx │ │ │ └── layout.tsx │ │ ├── robots.ts │ │ ├── api │ │ │ └── auth │ │ │ │ └── [...nextauth] │ │ │ │ └── route.ts │ │ ├── global-error.tsx │ │ └── _actions │ │ │ └── post.tsx │ ├── config │ │ └── site.ts │ ├── providers │ │ ├── auth-session-provider.tsx │ │ └── theme-provider.tsx │ ├── env.mjs │ ├── hooks │ │ └── useValidatedAction.ts │ └── styles │ │ └── globals.css │ ├── public │ ├── favicon.ico │ ├── site.webmanifest │ └── t3-icon.svg │ ├── postcss.config.cjs │ ├── types │ └── index.d.ts │ ├── tailwind.config.ts │ ├── tsconfig.json │ ├── README.md │ ├── next.config.mjs │ └── package.json ├── packages ├── utils │ ├── index.ts │ ├── src │ │ └── utils.ts │ ├── tsconfig.json │ └── package.json ├── db │ ├── tsconfig.json │ ├── index.ts │ ├── package.json │ └── prisma │ │ └── schema.prisma ├── auth │ ├── tsconfig.json │ ├── index.ts │ ├── src │ │ ├── session.ts │ │ └── auth-options.ts │ ├── package.json │ └── env.mjs ├── config │ ├── tailwind │ │ ├── postcss.js │ │ ├── package.json │ │ └── index.ts │ └── eslint │ │ ├── package.json │ │ └── index.js └── ui │ ├── src │ ├── aspect-ratio.tsx │ ├── skeleton.tsx │ ├── collapsible.tsx │ ├── tailwind-indicator.tsx │ ├── label.tsx │ ├── textarea.tsx │ ├── input.tsx │ ├── progress.tsx │ ├── separator.tsx │ ├── toaster.tsx │ ├── hover-card.tsx │ ├── tooltip.tsx │ ├── checkbox.tsx │ ├── popover.tsx │ ├── slider.tsx │ ├── badge.tsx │ ├── switch.tsx │ ├── avatar.tsx │ ├── toggle.tsx │ ├── radio-group.tsx │ ├── scroll-area.tsx │ ├── alert.tsx │ ├── button.tsx │ ├── tabs.tsx │ ├── accordion.tsx │ ├── card.tsx │ ├── calendar.tsx │ ├── table.tsx │ ├── dialog.tsx │ ├── select.tsx │ ├── hooks │ │ └── use-toast.ts │ ├── alert-dialog.tsx │ ├── toast.tsx │ ├── command.tsx │ ├── navigation-menu.tsx │ ├── sheet.tsx │ ├── context-menu.tsx │ ├── dropdown-menu.tsx │ └── menubar.tsx │ ├── tsconfig.json │ ├── package.json │ └── index.ts ├── vercel.json ├── pnpm-workspace.yaml ├── .github ├── deployment.jpg └── workflows │ └── ci.yml ├── .editorconfig ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── .eslintrc.js ├── tsconfig.json ├── .gitignore ├── .npmrc ├── .env.example ├── turbo.json ├── package.json ├── prettier.config.cjs └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /apps/web/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { cn } from "./src/utils"; 2 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "silent": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - apps/* 3 | - packages/* 4 | - packages/config/* 5 | -------------------------------------------------------------------------------- /apps/web/src/features/profile/index.ts: -------------------------------------------------------------------------------- 1 | export { AuthShowcase } from "./components/auth-showcase"; 2 | -------------------------------------------------------------------------------- /packages/db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["index.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /.github/deployment.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braydenwerner/t3-turbo-next-app-router/HEAD/.github/deployment.jpg -------------------------------------------------------------------------------- /packages/auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src", "*.ts", "env.mjs"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braydenwerner/t3-turbo-next-app-router/HEAD/apps/web/public/favicon.ico -------------------------------------------------------------------------------- /apps/web/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | // @ts-expect-error - No types for postcss 2 | module.exports = require("@acme/tailwind-config/postcss"); 3 | -------------------------------------------------------------------------------- /apps/web/src/features/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { Icons } from "./components/icons"; 2 | export { ThemeToggle } from "./components/theme-toggle"; 3 | -------------------------------------------------------------------------------- /packages/config/tailwind/postcss.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/auth/index.ts: -------------------------------------------------------------------------------- 1 | export { authOptions } from "./src/auth-options"; 2 | export { getServerSession, getCurrentUser } from "./src/session"; 3 | -------------------------------------------------------------------------------- /apps/web/src/app/(routes)/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@acme/ui"; 2 | 3 | export default function Loading() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/src/features/posts/index.ts: -------------------------------------------------------------------------------- 1 | export { CreatePostForm } from "./components/create-post-form"; 2 | export { PostCard } from "./components/post-card"; 3 | -------------------------------------------------------------------------------- /packages/ui/src/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /packages/utils/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /apps/web/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export type SiteConfig = { 2 | name: string; 3 | description: string; 4 | url: string; 5 | ogImage: string; 6 | links: { 7 | twitter: string; 8 | github: string; 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "dbaeumer.vscode-eslint", 5 | "bradlc.vscode-tailwindcss", 6 | "Prisma.prisma", 7 | "yoavbls.pretty-ts-errors" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/src/app/robots.ts: -------------------------------------------------------------------------------- 1 | import type { MetadataRoute } from "next"; 2 | 3 | export default function robots(): MetadataRoute.Robots { 4 | return { 5 | rules: { 6 | userAgent: "*", 7 | allow: "/", 8 | }, 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | import baseConfig from "@acme/tailwind-config"; 4 | 5 | export default { 6 | content: ["./src/**/*.tsx", "../../packages/ui/**/*.{js,ts,jsx,tsx}"], 7 | presets: [baseConfig], 8 | } satisfies Config; 9 | -------------------------------------------------------------------------------- /apps/web/src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth"; 2 | 3 | import { authOptions } from "@acme/auth"; 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 6 | const handler = NextAuth(authOptions); 7 | 8 | export { handler as GET, handler as POST }; 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Next.js", 6 | "type": "node-terminal", 7 | "request": "launch", 8 | "command": "pnpm dev", 9 | "cwd": "${workspaceFolder}/apps/nextjs/", 10 | "skipFiles": ["/**"] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/src/app/(routes)/error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export default function Error({ 4 | error, 5 | reset, 6 | }: { 7 | error: Error; 8 | reset: () => void; 9 | }) { 10 | return ( 11 |
12 |

Something went wrong!

13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /apps/web/src/lib/fonts.ts: -------------------------------------------------------------------------------- 1 | import { 2 | JetBrains_Mono as FontMono, 3 | Inter as FontSans, 4 | } from "next/font/google"; 5 | 6 | export const fontSans = FontSans({ 7 | subsets: ["latin"], 8 | variable: "--font-sans", 9 | }); 10 | 11 | export const fontMono = FontMono({ 12 | subsets: ["latin"], 13 | variable: "--font-mono", 14 | }); 15 | -------------------------------------------------------------------------------- /apps/web/src/config/site.ts: -------------------------------------------------------------------------------- 1 | import { type SiteConfig } from "types"; 2 | 3 | export const siteConfig: SiteConfig = { 4 | name: "t3-turbo-next-app-router-with-server-components", 5 | description: "Template using T3, turbo, Next app router", 6 | url: "", 7 | ogImage: "", 8 | links: { 9 | twitter: "", 10 | github: "", 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /packages/ui/src/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@acme/utils"; 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ); 13 | } 14 | 15 | export { Skeleton }; 16 | -------------------------------------------------------------------------------- /apps/web/src/providers/auth-session-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { SessionProvider } from "next-auth/react"; 4 | 5 | export interface AuthSessionProviderProps { 6 | children: React.ReactNode; 7 | } 8 | 9 | export function AuthSessionProvider({ children }: AuthSessionProviderProps) { 10 | return {children}; 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/src/app/global-error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export default function GlobalError({ 4 | error, 5 | reset, 6 | }: { 7 | error: Error; 8 | reset: () => void; 9 | }) { 10 | return ( 11 | 12 | 13 |

Something went wrong!

14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/ui/src/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /apps/web/src/providers/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 5 | import { type ThemeProviderProps } from "next-themes/dist/types"; 6 | 7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 8 | return {children}; 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "~/*": ["./src/*"] 7 | }, 8 | "plugins": [{ "name": "next" }] 9 | }, 10 | "exclude": [], 11 | "include": [ 12 | "next-env.d.ts", 13 | "**/*.ts", 14 | "**/*.tsx", 15 | "**/*.cjs", 16 | "**/*.mjs", 17 | ".next/types/**/*.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "~/*": ["./src/*"] 7 | }, 8 | "plugins": [{ "name": "next" }] 9 | }, 10 | "exclude": [], 11 | "include": [ 12 | "next-env.d.ts", 13 | "**/*.ts", 14 | "**/*.tsx", 15 | "**/*.cjs", 16 | "**/*.mjs", 17 | ".next/types/**/*.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/config/tailwind/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@acme/tailwind-config", 3 | "version": "0.1.0", 4 | "main": "index.ts", 5 | "license": "MIT", 6 | "files": [ 7 | "index.ts", 8 | "postcss.js" 9 | ], 10 | "devDependencies": { 11 | "autoprefixer": "^10.4.14", 12 | "postcss": "^8.4.23", 13 | "tailwindcss": "^3.3.2" 14 | }, 15 | "dependencies": { 16 | "tailwindcss-animate": "^1.0.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/db/index.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | export * from "@prisma/client"; 4 | 5 | const globalForPrisma = globalThis as { prisma?: PrismaClient }; 6 | 7 | export const db = 8 | globalForPrisma.prisma || 9 | new PrismaClient({ 10 | log: 11 | process.env.NODE_ENV === "development" 12 | ? ["query", "error", "warn"] 13 | : ["error"], 14 | }); 15 | 16 | if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db; 17 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "~/*": ["./src/*"] 7 | }, 8 | "plugins": [ 9 | { 10 | "name": "next" 11 | } 12 | ], 13 | "strictNullChecks": true 14 | }, 15 | "exclude": [], 16 | "include": [ 17 | "next-env.d.ts", 18 | "**/*.ts", 19 | "**/*.tsx", 20 | "**/*.cjs", 21 | "**/*.mjs", 22 | ".next/types/**/*.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /apps/web/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "t3-turbo-next-app-router-with-shared-ui", 3 | "short_name": "t3-turbo-next-app-router-with-shared-ui", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /packages/config/eslint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@acme/eslint-config", 3 | "version": "0.1.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@types/eslint": "^8.37.0", 8 | "@typescript-eslint/eslint-plugin": "^5.59.2", 9 | "@typescript-eslint/parser": "^5.59.2", 10 | "eslint-config-next": "^13.4.2", 11 | "eslint-config-prettier": "^8.8.0", 12 | "eslint-config-turbo": "^1.9.4", 13 | "eslint-plugin-react": "7.32.2" 14 | }, 15 | "devDependencies": { 16 | "eslint": "^8.40.0", 17 | "typescript": "^5.0.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | const config = { 3 | root: true, 4 | extends: ["@acme/eslint-config"], // uses the config in `packages/config/eslint` 5 | parser: "@typescript-eslint/parser", 6 | parserOptions: { 7 | ecmaVersion: "latest", 8 | tsconfigRootDir: __dirname, 9 | project: [ 10 | "./tsconfig.json", 11 | "./apps/*/tsconfig.json", 12 | "./packages/*/tsconfig.json", 13 | ], 14 | }, 15 | settings: { 16 | web: { 17 | rootDir: ["apps/web"], 18 | }, 19 | }, 20 | }; 21 | 22 | module.exports = config; 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": "explicit" 4 | }, 5 | "editor.defaultFormatter": "esbenp.prettier-vscode", 6 | "editor.formatOnSave": true, 7 | "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], 8 | "eslint.workingDirectories": [ 9 | { "pattern": "apps/*/" }, 10 | { "pattern": "packages/*/" } 11 | ], 12 | "typescript.tsdk": "node_modules/typescript/lib", 13 | "typescript.enablePromptUseWorkspaceTsdk": true, 14 | "tailwindCSS.experimental.configFile": "./packages/config/tailwind/index.ts" 15 | } 16 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@acme/utils", 3 | "version": "0.1.0", 4 | "main": "./index.ts", 5 | "types": "./index.ts", 6 | "license": "MIT", 7 | "scripts": { 8 | "clean": "rm -rf .turbo node_modules", 9 | "lint": "eslint .", 10 | "lint:fix": "pnpm lint --fix", 11 | "type-check": "tsc --noEmit" 12 | }, 13 | "dependencies": { 14 | "clsx": "^1.2.1", 15 | "tailwind-merge": "^1.12.0" 16 | }, 17 | "devDependencies": { 18 | "@acme/eslint-config": "workspace:*", 19 | "@types/node": "^18.16.5", 20 | "eslint": "^8.40.0", 21 | "typescript": "^5.0.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "checkJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "noUncheckedIndexedAccess": true 19 | }, 20 | "include": [".eslintrc.js", "prettier.config.cjs"] 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/README.md: -------------------------------------------------------------------------------- 1 | ### About 2 | 3 | ``` 4 | ├── public 5 | ├── src 6 | │   ├── app 7 | │   │   ├── (routes) (pages and layouts) 8 | │   │   ├── _actions (server actions) 9 | │   │   └── api 10 | │   │   └── auth 11 | │   │   └── [...nextauth] 12 | │   ├── config (configuation such as site metadata) 13 | │   ├── features 14 | │   │   ├── posts 15 | │   │   │   └── components 16 | │   │   ├── profile 17 | │   │   │   └── components 18 | │   │   └── ui 19 | │   │   └── components 20 | │   ├── lib (Utilities, library wrappers, etc...) 21 | │   ├── providers (React context providers) 22 | │   └── styles (globals.css file with color scheme) 23 | └── types 24 | ``` 25 | -------------------------------------------------------------------------------- /packages/db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@acme/db", 3 | "version": "0.1.0", 4 | "main": "./index.ts", 5 | "types": "./index.ts", 6 | "license": "MIT", 7 | "scripts": { 8 | "clean": "rm -rf .turbo node_modules", 9 | "db:generate": "pnpm with-env prisma generate", 10 | "db:push": "pnpm with-env prisma db push --skip-generate", 11 | "dev": "pnpm with-env prisma studio --port 5556", 12 | "with-env": "dotenv -e ../../.env --" 13 | }, 14 | "dependencies": { 15 | "@prisma/client": "^4.13.0" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^18.16.5", 19 | "dotenv-cli": "^7.2.1", 20 | "prisma": "^4.13.0", 21 | "typescript": "^5.0.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # database 12 | **/prisma/db.sqlite 13 | **/prisma/db.sqlite-journal 14 | 15 | # next.js 16 | .next/ 17 | out/ 18 | next-env.d.ts 19 | 20 | # expo 21 | .expo/ 22 | dist/ 23 | 24 | # production 25 | build 26 | 27 | # misc 28 | .DS_Store 29 | *.pem 30 | 31 | # debug 32 | npm-debug.log* 33 | yarn-debug.log* 34 | yarn-error.log* 35 | .pnpm-debug.log* 36 | 37 | # local env files 38 | .env 39 | .env*.local 40 | 41 | # vercel 42 | .vercel 43 | 44 | # typescript 45 | *.tsbuildinfo 46 | 47 | # turbo 48 | .turbo 49 | -------------------------------------------------------------------------------- /apps/web/src/app/_actions/post.tsx: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { revalidatePath } from "next/cache"; 4 | import { z } from "zod"; 5 | 6 | import { db } from "@acme/db"; 7 | 8 | import { validate } from "~/lib/validateServerAction"; 9 | 10 | export const getPosts = validate()(async () => { 11 | return db.post.findMany({ orderBy: { id: "desc" } }); 12 | }); 13 | 14 | export const createPost = validate( 15 | z.object({ title: z.string().min(1), content: z.string().min(1) }), 16 | )(async (data) => { 17 | await db.post.create({ data }); 18 | revalidatePath("/"); 19 | }); 20 | 21 | export const deletePost = validate(z.string())(async (id) => { 22 | await db.post.delete({ where: { id } }); 23 | revalidatePath("/"); 24 | }); 25 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Expo doesn't play nice with pnpm by default. 2 | # The symbolic links of pnpm break the rules of Expo monorepos. 3 | # @link https://docs.expo.dev/guides/monorepos/#common-issues 4 | node-linker=hoisted 5 | 6 | # In order to cache Prisma correctly 7 | public-hoist-pattern[]=*prisma* 8 | 9 | # FIXME: @prisma/client is required by the @acme/auth, 10 | # but we don't want it installed there since it's already 11 | # installed in the @acme/db package 12 | strict-peer-dependencies=false 13 | 14 | # Prevent pnpm from adding the "workspace:"" prefix to local 15 | # packages as it causes issues with manypkg 16 | # @link https://pnpm.io/npmrc#prefer-workspace-packages 17 | save-workspace-protocol=false 18 | prefer-workspace-packages=true 19 | -------------------------------------------------------------------------------- /packages/auth/src/session.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | GetServerSidePropsContext, 3 | NextApiRequest, 4 | NextApiResponse, 5 | } from "next"; 6 | import { getServerSession as $getServerSession } from "next-auth"; 7 | 8 | import { authOptions } from "./auth-options"; 9 | 10 | type GetServerSessionContext = 11 | | { 12 | req: GetServerSidePropsContext["req"]; 13 | res: GetServerSidePropsContext["res"]; 14 | } 15 | | { req: NextApiRequest; res: NextApiResponse }; 16 | export const getServerSession = (ctx: GetServerSessionContext) => { 17 | return $getServerSession(ctx.req, ctx.res, authOptions); 18 | }; 19 | 20 | export async function getCurrentUser() { 21 | const session = await $getServerSession(authOptions); 22 | 23 | return session?.user; 24 | } 25 | -------------------------------------------------------------------------------- /packages/ui/src/tailwind-indicator.tsx: -------------------------------------------------------------------------------- 1 | export function TailwindIndicator() { 2 | if (process.env.NODE_ENV === "production") return null; 3 | 4 | return ( 5 |
6 |
xs
7 |
8 | sm 9 |
10 |
md
11 |
lg
12 |
xl
13 |
2xl
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@acme/auth", 3 | "version": "0.1.0", 4 | "main": "./index.ts", 5 | "types": "./index.ts", 6 | "license": "MIT", 7 | "scripts": { 8 | "clean": "rm -rf .turbo node_modules", 9 | "lint": "eslint .", 10 | "lint:fix": "pnpm lint --fix", 11 | "type-check": "tsc --noEmit" 12 | }, 13 | "dependencies": { 14 | "@acme/db": "workspace:*", 15 | "@next-auth/prisma-adapter": "^1.0.6", 16 | "@t3-oss/env-nextjs": "^0.3.1", 17 | "next": "^13.4.2", 18 | "next-auth": "^4.22.1", 19 | "react": "18.2.0", 20 | "react-dom": "18.2.0", 21 | "zod": "^3.21.4" 22 | }, 23 | "devDependencies": { 24 | "@acme/eslint-config": "workspace:*", 25 | "eslint": "^8.40.0", 26 | "typescript": "^5.0.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/web/src/features/profile/components/auth-showcase.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { signIn, signOut, useSession } from "next-auth/react"; 4 | 5 | import { Button } from "@acme/ui"; 6 | 7 | export function AuthShowcase() { 8 | const { data } = useSession(); 9 | 10 | return ( 11 |
12 | {data?.user && ( 13 |

14 | {Logged in as {data.user.name}} 15 |

16 | )} 17 | 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/ui/src/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as LabelPrimitive from "@radix-ui/react-label"; 5 | import { cva, type VariantProps } from "class-variance-authority"; 6 | 7 | import { cn } from "@acme/utils"; 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 11 | ); 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )); 24 | Label.displayName = LabelPrimitive.Root.displayName; 25 | 26 | export { Label }; 27 | -------------------------------------------------------------------------------- /packages/ui/src/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@acme/utils"; 4 | 5 | export type TextareaProps = React.TextareaHTMLAttributes; 6 | 7 | const Textarea = React.forwardRef( 8 | ({ className, ...props }, ref) => { 9 | return ( 10 |