├── packages ├── lib │ ├── index.ts │ ├── tsconfig.json │ ├── errors │ │ └── index.ts │ ├── package.json │ ├── stripe │ │ ├── plans.ts │ │ └── get-user-plan.ts │ ├── auth │ │ ├── send-verification-email.ts │ │ └── verify-email.ts │ └── validators │ │ └── user.ts ├── db │ ├── index.ts │ ├── .gitignore │ ├── .env.example │ ├── seed.ts │ ├── client.ts │ ├── package.json │ └── prisma │ │ └── schema.prisma ├── auth │ ├── src │ │ ├── index.ts │ │ ├── get-server-session.ts │ │ ├── next-auth.d.ts │ │ ├── crypto.ts │ │ └── next-auth-options.ts │ ├── tsconfig.json │ └── package.json ├── ui │ ├── postcss.config.cjs │ ├── tsconfig.json │ ├── lib │ │ └── utils.ts │ ├── tailwind.config.ts │ ├── components.json │ ├── components │ │ ├── cancel-button.tsx │ │ ├── label.tsx │ │ ├── separator.tsx │ │ ├── textarea.tsx │ │ ├── toggle-group.tsx │ │ ├── switch.tsx │ │ ├── badge.tsx │ │ ├── avatar.tsx │ │ ├── toggle.tsx │ │ ├── input.tsx │ │ ├── alert.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── tabs.tsx │ │ ├── dialog.tsx │ │ ├── alert-dialog.tsx │ │ ├── form.tsx │ │ └── dropdown-menu.tsx │ ├── index.ts │ └── package.json ├── emails │ ├── postcss.config.cjs │ ├── index.ts │ ├── tsconfig.json │ ├── tailwind.config.ts │ ├── package.json │ └── template │ │ ├── verify-email.tsx │ │ └── forgot-password-email.tsx └── api │ ├── tsconfig.json │ ├── src │ ├── root.ts │ ├── routers │ │ ├── stripe │ │ │ ├── webhook.ts │ │ │ └── index.ts │ │ ├── task.ts │ │ └── user.ts │ └── trpc.ts │ ├── index.ts │ └── package.json ├── .prettierignore ├── .npmrc ├── tsconfig.json ├── pnpm-workspace.yaml ├── tooling ├── tsconfig │ ├── package.json │ ├── lib.json │ ├── nextjs.json │ └── base.json ├── tailwind │ ├── package.json │ └── tailwind.config.ts └── eslint │ ├── package.json │ └── index.cjs ├── .husky └── pre-commit ├── apps └── web │ ├── public │ └── favicon.ico │ ├── postcss.config.cjs │ ├── next-env.d.ts │ ├── src │ ├── app │ │ ├── api │ │ │ ├── auth │ │ │ │ └── [...nextauth] │ │ │ │ │ └── route.ts │ │ │ ├── trpc │ │ │ │ └── [trpc] │ │ │ │ │ └── route.ts │ │ │ └── webhooks │ │ │ │ └── stripe │ │ │ │ └── route.ts │ │ ├── _components │ │ │ ├── background.tsx │ │ │ ├── theme-toggle.tsx │ │ │ ├── footer.tsx │ │ │ ├── nav.tsx │ │ │ └── user-account-dropdown.tsx │ │ ├── providers.tsx │ │ ├── tasks │ │ │ ├── page.tsx │ │ │ └── _components │ │ │ │ ├── todolist.tsx │ │ │ │ ├── create-task.tsx │ │ │ │ └── task.tsx │ │ ├── not-found.tsx │ │ ├── (auth) │ │ │ ├── forgot-password │ │ │ │ └── page.tsx │ │ │ ├── _components │ │ │ │ ├── github-button.tsx │ │ │ │ ├── forgot-password-form.tsx │ │ │ │ ├── login-form.tsx │ │ │ │ ├── reset-password-form.tsx │ │ │ │ └── signup-form.tsx │ │ │ ├── login │ │ │ │ └── page.tsx │ │ │ ├── signup │ │ │ │ └── page.tsx │ │ │ ├── reset-password │ │ │ │ └── page.tsx │ │ │ └── verify-email │ │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── settings │ │ │ ├── _components │ │ │ │ ├── profile-settings.tsx │ │ │ │ ├── delete-account.tsx │ │ │ │ ├── billing.tsx │ │ │ │ └── add-or-update-password-modal.tsx │ │ │ └── page.tsx │ │ └── page.tsx │ ├── components │ │ ├── shell.tsx │ │ ├── loading-animation.tsx │ │ └── verify-email-banner.tsx │ ├── trpc │ │ ├── get-ending-link.ts │ │ ├── server.ts │ │ └── react.tsx │ ├── lib │ │ └── utils.ts │ ├── middleware.ts │ └── globals.css │ ├── next.config.mjs │ ├── tailwind.config.ts │ ├── tsconfig.json │ ├── .gitignore │ ├── .env.example │ └── package.json ├── .gitignore ├── .eslintrc.cjs ├── turbo.json ├── .prettierrc ├── package.json ├── LICENSE └── readme.md /packages/lib/index.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .next 4 | build -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true 2 | public-hoist-pattern[] = *prisma* 3 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@blueprint/tsconfig/base.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/db/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@prisma/client"; 2 | 3 | export { db } from "./client"; 4 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | - "tooling/*" 5 | -------------------------------------------------------------------------------- /packages/db/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | # Keep environment variables out of version control 3 | .env 4 | -------------------------------------------------------------------------------- /tooling/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blueprint/tsconfig", 3 | "version": "0.0.1" 4 | } 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm lint 5 | pnpm build 6 | -------------------------------------------------------------------------------- /apps/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackquinlan/blueprint/HEAD/apps/web/public/favicon.ico -------------------------------------------------------------------------------- /packages/auth/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./get-server-session"; 2 | export { authOptions } from "./next-auth-options"; 3 | -------------------------------------------------------------------------------- /packages/ui/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | tailwindcss: {}, 5 | }, 6 | }; -------------------------------------------------------------------------------- /apps/web/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | -------------------------------------------------------------------------------- /packages/auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@blueprint/tsconfig/base.json", 3 | "compilerOptions": {}, 4 | "include": ["src/**/*.ts"], 5 | } -------------------------------------------------------------------------------- /packages/emails/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | tailwindcss: {}, 5 | }, 6 | }; -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@blueprint/tsconfig/lib.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"], 5 | } -------------------------------------------------------------------------------- /packages/emails/index.ts: -------------------------------------------------------------------------------- 1 | export { ResetPasswordEmail } from "./template/forgot-password-email"; 2 | export { VerifyAccountEmail } from "./template/verify-email"; 3 | -------------------------------------------------------------------------------- /packages/emails/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@blueprint/tsconfig/lib.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"], 5 | } -------------------------------------------------------------------------------- /packages/lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@blueprint/tsconfig/lib.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"], 5 | } -------------------------------------------------------------------------------- /packages/lib/errors/index.ts: -------------------------------------------------------------------------------- 1 | export class RequiresPremiumError extends Error { 2 | constructor(message = "You must be a premium subscriber to use this functionality.") { 3 | super(message); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/ui/lib/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 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/web/src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth"; 2 | 3 | import { authOptions } from "@blueprint/auth"; 4 | 5 | const handler = NextAuth(authOptions); 6 | 7 | export { handler as GET, handler as POST }; 8 | -------------------------------------------------------------------------------- /packages/ui/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | import base from "@blueprint/tailwind/tailwind.config.ts"; 4 | 5 | const config: Pick = { 6 | presets: [base], 7 | }; 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /packages/emails/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | import base from "@blueprint/tailwind/tailwind.config.ts"; 4 | 5 | const config: Pick = { 6 | presets: [base], 7 | }; 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /packages/db/.env.example: -------------------------------------------------------------------------------- 1 | # Blueprint is setup to use PlanetScale by default, but you can modify it to use any database. 2 | # Visit https://planetscale.com/ to get started with your database. 3 | # If you don't want to use PlanetScale, just add your MySQL database URL here. 4 | DATABASE_URL="" -------------------------------------------------------------------------------- /packages/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@blueprint/tsconfig/nextjs.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["src/*"] 7 | }, 8 | }, 9 | "include": ["src", "*.ts", "../../packages/auth/src/next-auth.d.ts"] 10 | } -------------------------------------------------------------------------------- /apps/web/src/app/_components/background.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export function Background() { 4 | return ( 5 |
6 |
7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /tooling/tailwind/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blueprint/tailwind", 3 | "version": "1.0.0", 4 | "devDependencies": { 5 | "tailwindcss": "^3.3.3" 6 | }, 7 | "dependencies": { 8 | "tailwindcss-animate": "^1.0.7" 9 | }, 10 | "publishConfig": { 11 | "access": "public" 12 | } 13 | } -------------------------------------------------------------------------------- /tooling/tsconfig/lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "jsx": "react-jsx", 7 | "lib": ["ES2015", "DOM"], 8 | "target": "es6", 9 | "module": "ESNext" 10 | } 11 | } -------------------------------------------------------------------------------- /apps/web/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const config = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | transpilePackages: ["@blueprint/ui"], 6 | images: { 7 | domains: ["lh3.googleusercontent.com", "avatars.githubusercontent.com"], 8 | } 9 | }; 10 | 11 | export default config; -------------------------------------------------------------------------------- /packages/lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blueprint/lib", 3 | "version": "1.0.0", 4 | "main": "src/index.ts", 5 | "dependencies": { 6 | "@blueprint/db": "workspace:*", 7 | "@blueprint/emails": "workspace:*", 8 | "date-fns": "^2.30.0", 9 | "resend": "^2.0.0", 10 | "zod": "^3.22.4" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "20.8.0" 14 | } 15 | } -------------------------------------------------------------------------------- /packages/auth/src/get-server-session.ts: -------------------------------------------------------------------------------- 1 | import { getServerSession } from "next-auth"; 2 | 3 | import { authOptions } from "./next-auth-options"; 4 | 5 | /** 6 | * Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file. 7 | * 8 | * @see https://next-auth.js.org/configuration/nextjs 9 | */ 10 | export const getServerAuthSession = () => { 11 | return getServerSession(authOptions); 12 | }; 13 | -------------------------------------------------------------------------------- /packages/api/src/root.ts: -------------------------------------------------------------------------------- 1 | // prettier-ignore 2 | import { createRouter } from "./trpc"; 3 | 4 | import { stripeRouter } from "./routers/stripe"; 5 | import { taskRouter } from "./routers/task"; 6 | import { userRouter } from "./routers/user"; 7 | 8 | const appRouter = createRouter({ 9 | stripe: stripeRouter, 10 | user: userRouter, 11 | task: taskRouter, 12 | }); 13 | export type AppRouter = typeof appRouter; 14 | export { appRouter }; 15 | -------------------------------------------------------------------------------- /packages/ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "src/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } -------------------------------------------------------------------------------- /apps/web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | import base from "@blueprint/tailwind/tailwind.config.ts"; 4 | 5 | const config: Pick = { 6 | presets: [ 7 | { 8 | ...base, 9 | content: [ 10 | "./src/**/*.{js,ts,jsx,tsx,mdx}", 11 | "../../packages/ui/**/*.{js,ts,jsx,tsx,mdx}", 12 | ], 13 | }, 14 | ], 15 | }; 16 | 17 | export default config; 18 | -------------------------------------------------------------------------------- /packages/auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blueprint/auth", 3 | "version": "1.0.0", 4 | "main": "src/index.ts", 5 | "dependencies": { 6 | "@blueprint/db": "workspace:*", 7 | "@next-auth/prisma-adapter": "^1.0.7", 8 | "hash-wasm": "^4.11.0", 9 | "next-auth": "^4.24.5" 10 | }, 11 | "devDependencies": { 12 | "@blueprint/tsconfig": "workspace:*", 13 | "@types/node": "20.8.0", 14 | "typescript": "5.2.2" 15 | } 16 | } -------------------------------------------------------------------------------- /apps/web/src/app/providers.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | 5 | import { SessionProvider } from "next-auth/react"; 6 | import { ThemeProvider as NextThemeProvider } from "next-themes"; 7 | 8 | export function Providers({ children }: { children: React.ReactNode }) { 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/db/seed.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | const prisma = new PrismaClient(); 4 | async function main() { 5 | await prisma.example.create({ 6 | data: { 7 | text: "This is an example model.", 8 | }, 9 | }); 10 | } 11 | 12 | main() 13 | .then(async () => { 14 | await prisma.$disconnect(); 15 | }) 16 | .catch(async (e) => { 17 | console.error(e); 18 | await prisma.$disconnect(); 19 | process.exit(1); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/auth/src/next-auth.d.ts: -------------------------------------------------------------------------------- 1 | import type { DefaultUser } from "next-auth"; 2 | 3 | declare module "next-auth" { 4 | interface Session { 5 | user: User; 6 | } 7 | interface User extends Omit { 8 | id: string; 9 | email: string; 10 | emailVerified?: string | null; 11 | } 12 | } 13 | 14 | declare module "next-auth/jwt" { 15 | interface JWT { 16 | id: string; 17 | email: string; 18 | emailVerified?: string | null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/web/src/components/shell.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | interface ShellProps extends React.HTMLAttributes {} 6 | 7 | function Shell({ className = "", children, ...props }: ShellProps) { 8 | return ( 9 |
13 | {children} 14 |
15 | ); 16 | } 17 | export { Shell, type ShellProps }; 18 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@blueprint/tsconfig/nextjs.json", 3 | "compilerOptions": { 4 | "strictNullChecks": true, 5 | "strict": true, 6 | "plugins": [{ "name": "next" }], 7 | "baseUrl": ".", 8 | "paths": { 9 | "@/*": ["src/*"], 10 | }, 11 | "allowImportingTsExtensions": true, 12 | }, 13 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "../../packages/auth/src/next-auth.d.ts"], 14 | "exclude": ["node_modules"] 15 | } 16 | -------------------------------------------------------------------------------- /.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 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # local env files 26 | .env 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | # turbo 33 | .turbo 34 | 35 | # vercel 36 | .vercel 37 | -------------------------------------------------------------------------------- /apps/web/.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 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | const config = { 3 | root: true, 4 | extends: ["@blueprint"], // uses the config in `packages/config/eslint` 5 | parser: "@typescript-eslint/parser", 6 | parserOptions: { 7 | ecmaVersion: "latest", 8 | tsconfigRootDir: __dirname, 9 | project: ["./apps/*/tsconfig.json", "./packages/**/*/tsconfig.json"], 10 | }, 11 | settings: { 12 | next: { 13 | rootDir: ["apps/*/"], 14 | }, 15 | }, 16 | }; 17 | 18 | module.exports = config; 19 | -------------------------------------------------------------------------------- /apps/web/src/app/tasks/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { redirect } from "next/navigation"; 3 | 4 | import { getServerAuthSession } from "@blueprint/auth"; 5 | 6 | import { Todolist } from "@/app/tasks/_components/todolist"; 7 | 8 | export default async function Tasks() { 9 | const session = await getServerAuthSession(); 10 | if (!session) { 11 | return redirect("/"); 12 | } 13 | return ( 14 |
15 | 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalDependencies": ["**/.env.*local"], 4 | "pipeline": { 5 | "build": { 6 | "dependsOn": ["^build", "^db:generate"], 7 | "outputs": [".next/**", "!.next/cache/**"] 8 | }, 9 | "lint": {}, 10 | "dev": { 11 | "cache": false, 12 | "dependsOn": ["^db:generate"] 13 | }, 14 | "db:generate": { 15 | "cache": false 16 | }, 17 | "db:push": { 18 | "cache": false 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/db/client.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | // Make sure that there is only one instance of Prisma Client at any one time. 4 | declare global { 5 | var __globalPrisma__: PrismaClient; 6 | } 7 | 8 | export let db: PrismaClient; 9 | 10 | if (process.env.NODE_ENV === "production") { 11 | db = new PrismaClient({ 12 | log: ["error", "warn"], 13 | }); 14 | } else { 15 | if (!global.__globalPrisma__) { 16 | global.__globalPrisma__ = new PrismaClient({ 17 | log: ["error", "warn"], 18 | }); 19 | } 20 | 21 | db = global.__globalPrisma__; 22 | } 23 | -------------------------------------------------------------------------------- /apps/web/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { X } from "lucide-react"; 2 | 3 | export default function NotFound() { 4 | return ( 5 |
6 | 7 |

8 | 404 - Page Not Found 9 |

10 |

11 | Sorry, the page you are looking for does not exist. 12 |

13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blueprint/db", 3 | "version": "1.0.0", 4 | "main": "./index.ts", 5 | "types": "./index.ts", 6 | "scripts": { 7 | "db:generate": "prisma generate", 8 | "db:push": "prisma db push --skip-generate", 9 | "db:seed": "pnpx prisma db seed", 10 | "db:format": "pnpx prisma format" 11 | }, 12 | "prisma": { 13 | "seed": "ts-node --transpile-only ./seed.ts" 14 | }, 15 | "devDependencies": { 16 | "prisma": "5.5.2" 17 | }, 18 | "dependencies": { 19 | "@prisma/client": "5.9.1", 20 | "ts-node": "^10.9.1" 21 | } 22 | } -------------------------------------------------------------------------------- /packages/auth/src/crypto.ts: -------------------------------------------------------------------------------- 1 | import crypto from "crypto"; 2 | 3 | import { bcrypt, bcryptVerify } from "hash-wasm"; 4 | 5 | const COST_FACTOR = 11; 6 | 7 | export async function hashPassword(password: string): Promise { 8 | const salt = crypto.randomBytes(16); 9 | 10 | const hash = await bcrypt({ 11 | password, 12 | salt, 13 | costFactor: COST_FACTOR, 14 | outputType: "encoded", 15 | }); 16 | 17 | return hash; 18 | } 19 | 20 | export function verifyPassword(hashedPassword: string, password: string): Promise { 21 | return bcryptVerify({ 22 | password, 23 | hash: hashedPassword, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /packages/ui/components/cancel-button.tsx: -------------------------------------------------------------------------------- 1 | import { Button, ButtonProps } from "./button"; 2 | 3 | interface CancelButtonProps extends ButtonProps { 4 | close: () => void; 5 | } 6 | 7 | export function CancelButton({ 8 | close, 9 | variant = "outline", 10 | size = "xs", 11 | ...props 12 | }: CancelButtonProps) { 13 | return ( 14 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /packages/emails/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blueprint/emails", 3 | "version": "1.0.0", 4 | "main": "index.ts", 5 | "dependencies": { 6 | "@react-email/components": "^0.0.11", 7 | "postcss": "^8.4.31", 8 | "react": "18.2.0", 9 | "react-dom": "18.2.0", 10 | "react-email": "^1.9.5", 11 | "tailwindcss": "3.3.2" 12 | }, 13 | "devDependencies": { 14 | "@blueprint/eslint-config": "workspace:*", 15 | "@blueprint/tailwind": "workspace:*", 16 | "@blueprint/tsconfig": "workspace:*", 17 | "@types/node": "20.8.0", 18 | "@types/react": "18.2.24", 19 | "@types/react-dom": "18.2.8" 20 | } 21 | } -------------------------------------------------------------------------------- /tooling/eslint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blueprint/eslint-config", 3 | "version": "0.0.1", 4 | "main": "index.cjs", 5 | "dependencies": { 6 | "@types/eslint": "8.44.3", 7 | "@typescript-eslint/eslint-plugin": "6.7.3", 8 | "@typescript-eslint/parser": "6.7.3", 9 | "eslint-config-next": "13.5.3", 10 | "eslint-config-prettier": "9.0.0", 11 | "eslint-config-turbo": "1.10.14", 12 | "eslint-plugin-import": "2.28.1", 13 | "eslint-plugin-react": "7.33.2" 14 | }, 15 | "devDependencies": { 16 | "eslint": "8.50.0", 17 | "typescript": "5.2.2" 18 | }, 19 | "publishConfig": { 20 | "access": "public" 21 | } 22 | } -------------------------------------------------------------------------------- /packages/api/index.ts: -------------------------------------------------------------------------------- 1 | import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; 2 | 3 | import type { AppRouter } from "./src/root"; 4 | 5 | export { createRouter, createInnerTRPCContext, createTRPCContext } from "./src/trpc"; 6 | 7 | export { t } from "./src/trpc"; 8 | export { type AppRouter, appRouter } from "./src/root"; 9 | 10 | /** 11 | * Inference helpers for input types 12 | * @example type HelloInput = RouterInputs['example']['hello'] 13 | **/ 14 | export type RouterInputs = inferRouterInputs; 15 | 16 | /** 17 | * Inference helpers for output types 18 | * @example type HelloOutput = RouterOutputs['example']['hello'] 19 | **/ 20 | export type RouterOutputs = inferRouterOutputs; 21 | -------------------------------------------------------------------------------- /apps/web/src/app/api/trpc/[trpc]/route.ts: -------------------------------------------------------------------------------- 1 | import type { NextRequest } from "next/server"; 2 | 3 | import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; 4 | 5 | import { appRouter, createTRPCContext } from "@blueprint/api"; 6 | 7 | const handler = (req: NextRequest) => 8 | fetchRequestHandler({ 9 | endpoint: "/api/trpc", 10 | req, 11 | router: appRouter, 12 | createContext: () => createTRPCContext({ req }), 13 | onError: ({ error, path }) => { 14 | process.env.NODE_ENV === "development" 15 | ? console.error(`>>> tRPC Error on '${path}'`, error) 16 | : undefined; 17 | }, 18 | }); 19 | 20 | export { handler as GET, handler as POST }; 21 | -------------------------------------------------------------------------------- /tooling/tsconfig/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Next.js", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "plugins": [{ "name": "next" }], 7 | "allowJs": true, 8 | "declaration": false, 9 | "declarationMap": false, 10 | "incremental": true, 11 | "jsx": "preserve", 12 | "lib": ["dom", "dom.iterable", "esnext"], 13 | "module": "esnext", 14 | "noEmit": true, 15 | "resolveJsonModule": true, 16 | "strict": false, 17 | "target": "ES2021" 18 | }, 19 | "include": ["src", "next-env.d.ts", "../types/next-auth.d.ts", "../types/*.d.ts"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/lib/stripe/plans.ts: -------------------------------------------------------------------------------- 1 | export type SubscriptionPlan = { 2 | name: string; 3 | description: string; 4 | stripePriceId?: string; 5 | price: string; 6 | features: string[]; 7 | }; 8 | 9 | export const FREE_PLAN: SubscriptionPlan = { 10 | name: "Basic", 11 | description: "Get access to limited features for free.", 12 | stripePriceId: "", 13 | price: "Free", 14 | features: ["5 tasks", "An example basic feature"], 15 | }; 16 | 17 | export const PREMIUM_PLAN: SubscriptionPlan = { 18 | name: "Premium", 19 | description: "Get access to more features.", 20 | stripePriceId: process.env.PREMIUM_PLAN_PRICE_ID || "", 21 | price: "$9.99/mo", 22 | features: ["Unlimited tasks", "An awesome example premium feature"], 23 | }; 24 | -------------------------------------------------------------------------------- /packages/ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./components/alert-dialog"; 2 | export * from "./components/alert"; 3 | export * from "./components/avatar"; 4 | export * from "./components/button"; 5 | export * from "./components/badge"; 6 | export * from "./components/cancel-button"; 7 | export * from "./components/card"; 8 | export * from "./components/dropdown-menu"; 9 | export * from "./components/form"; 10 | export * from "./components/input"; 11 | export * from "./components/label"; 12 | export * from "./components/separator"; 13 | export * from "./components/toggle"; 14 | export * from "./components/toggle-group"; 15 | export * from "./components/tabs"; 16 | export * from "./components/textarea"; 17 | export * from "./components/dialog"; 18 | export * from "./components/switch"; 19 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "printWidth": 100, 4 | "useTabs": false, 5 | "tabWidth": 4, 6 | "trailingComma": "all", 7 | "singleQuote": false, 8 | "plugins": [ 9 | "@ianvs/prettier-plugin-sort-imports", 10 | "prettier-plugin-tailwindcss" 11 | ], 12 | "importOrderTypeScriptVersion": "4.4.0", 13 | "importOrder": [ 14 | "^(react/(.*)$)|^(react$)", 15 | "^(next/(.*)$)|^(next$)", 16 | "", 17 | "", 18 | "", 19 | "^@blueprint/(.*)$", 20 | "", 21 | "^@/(.*)$", 22 | "^@/lib/(.*)$", 23 | "^@/server/(.*)$", 24 | "^@/trpc/(.*)$", 25 | "^@/components/(.*)$", 26 | "^@/app/(.*)$", 27 | "^[./]" 28 | ] 29 | } -------------------------------------------------------------------------------- /tooling/tsconfig/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "declaration": true, 6 | "composite": false, 7 | "preserveWatchOutput": true, 8 | "declarationMap": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "allowImportingTsExtensions": true, 11 | "noEmit": true, 12 | "esModuleInterop": true, 13 | "isolatedModules": true, 14 | "noUnusedParameters": false, 15 | "inlineSources": false, 16 | "moduleResolution": "node", 17 | "noUnusedLocals": false, 18 | "skipLibCheck": true, 19 | "strict": true, 20 | "strictNullChecks": true 21 | }, 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /packages/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blueprint/api", 3 | "version": "1.0.0", 4 | "main": "index.ts", 5 | "types": "index.ts", 6 | "dependencies": { 7 | "@blueprint/auth": "workspace:*", 8 | "@blueprint/db": "workspace:*", 9 | "@blueprint/emails": "workspace:*", 10 | "@blueprint/lib": "workspace:*", 11 | "@trpc/client": "^10.40.0", 12 | "@trpc/server": "^10.40.0", 13 | "date-fns": "^2.30.0", 14 | "next": "^14.0.3", 15 | "next-auth": "^4.24.5", 16 | "resend": "^2.0.0", 17 | "stripe": "^14.1.0", 18 | "superjson": "^2.0.0", 19 | "zod": "^3.22.4" 20 | }, 21 | "devDependencies": { 22 | "@blueprint/tsconfig": "workspace:*", 23 | "typescript": "5.2.2" 24 | } 25 | } -------------------------------------------------------------------------------- /packages/ui/components/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | 5 | import * as LabelPrimitive from "@radix-ui/react-label"; 6 | import { cva, type VariantProps } from "class-variance-authority"; 7 | 8 | import { cn } from "../lib/utils"; 9 | 10 | const labelVariants = cva( 11 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 12 | ); 13 | 14 | const Label = React.forwardRef< 15 | React.ElementRef, 16 | React.ComponentPropsWithoutRef & VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 19 | )); 20 | Label.displayName = LabelPrimitive.Root.displayName; 21 | 22 | export { Label }; 23 | -------------------------------------------------------------------------------- /packages/ui/components/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | 5 | import * as SeparatorPrimitive from "@radix-ui/react-separator"; 6 | 7 | import { cn } from "../lib/utils"; 8 | 9 | const Separator = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => ( 13 | 24 | )); 25 | Separator.displayName = SeparatorPrimitive.Root.displayName; 26 | 27 | export { Separator }; 28 | -------------------------------------------------------------------------------- /apps/web/src/trpc/get-ending-link.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPBatchLinkOptions, HTTPHeaders, TRPCLink } from "@trpc/client"; 2 | import { httpBatchLink } from "@trpc/client"; 3 | 4 | import type { AppRouter } from "@blueprint/api"; 5 | 6 | import { getBaseUrl } from "@/lib/utils"; 7 | 8 | export const endingLink = (opts?: { headers?: HTTPHeaders }) => 9 | ((runtime) => { 10 | const sharedOpts = { 11 | headers: opts?.headers, 12 | } satisfies Partial; 13 | 14 | const trpcLink = httpBatchLink({ 15 | ...sharedOpts, 16 | url: `${getBaseUrl()}/api/trpc`, 17 | })(runtime); 18 | 19 | return (ctx) => { 20 | const path = ctx.op.path.split(".") as [string, ...string[]]; 21 | 22 | const newCtx = { 23 | ...ctx, 24 | op: { ...ctx.op, path: path.join(".") }, 25 | }; 26 | return trpcLink(newCtx); 27 | }; 28 | }) satisfies TRPCLink; 29 | -------------------------------------------------------------------------------- /packages/ui/components/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "../lib/utils"; 4 | 5 | export interface TextareaProps extends React.TextareaHTMLAttributes { 6 | ring?: boolean; 7 | } 8 | 9 | const Textarea = React.forwardRef( 10 | ({ className, ring = true, ...props }, ref) => { 11 | return ( 12 |