From 621b4626984a1e230ecb36977513967af935d49a Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 27 Oct 2023 22:01:59 +0100 Subject: [PATCH] Let it rip --- .env.example | 26 +++++ .eslintrc.cjs | 36 +++++++ .gitignore | 42 ++++++++ .vscode/settings.json | 3 + README.md | 3 + bun.lockb | Bin 0 -> 182885 bytes drizzle.config.ts | 12 +++ next.config.mjs | 10 ++ package.json | 52 ++++++++++ postcss.config.cjs | 8 ++ prettier.config.mjs | 6 ++ public/favicon.ico | Bin 0 -> 15406 bytes src/app/api/auth/[...nextauth]/route.ts | 11 ++ src/app/api/trpc/[trpc]/route.ts | 28 +++++ src/app/layout.tsx | 31 ++++++ src/app/page.tsx | 7 ++ src/env.mjs | 62 +++++++++++ src/server/api/root.ts | 14 +++ src/server/api/routers/board.ts | 14 +++ src/server/api/trpc.ts | 130 ++++++++++++++++++++++++ src/server/auth.ts | 68 +++++++++++++ src/server/db/index.ts | 12 +++ src/server/db/schema.ts | 105 +++++++++++++++++++ src/styles/globals.css | 3 + src/trpc/react.tsx | 47 +++++++++ src/trpc/server.ts | 28 +++++ src/trpc/shared.ts | 30 ++++++ tailwind.config.ts | 14 +++ tsconfig.json | 35 +++++++ 29 files changed, 837 insertions(+) create mode 100644 .env.example create mode 100644 .eslintrc.cjs create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100755 bun.lockb create mode 100644 drizzle.config.ts create mode 100644 next.config.mjs create mode 100644 package.json create mode 100644 postcss.config.cjs create mode 100644 prettier.config.mjs create mode 100644 public/favicon.ico create mode 100644 src/app/api/auth/[...nextauth]/route.ts create mode 100644 src/app/api/trpc/[trpc]/route.ts create mode 100644 src/app/layout.tsx create mode 100644 src/app/page.tsx create mode 100644 src/env.mjs create mode 100644 src/server/api/root.ts create mode 100644 src/server/api/routers/board.ts create mode 100644 src/server/api/trpc.ts create mode 100644 src/server/auth.ts create mode 100644 src/server/db/index.ts create mode 100644 src/server/db/schema.ts create mode 100644 src/styles/globals.css create mode 100644 src/trpc/react.tsx create mode 100644 src/trpc/server.ts create mode 100644 src/trpc/shared.ts create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..56d4230e --- /dev/null +++ b/.env.example @@ -0,0 +1,26 @@ +# Since the ".env" file is gitignored, you can use the ".env.example" file to +# build a new ".env" file when you clone the repo. Keep this file up-to-date +# when you add new variables to `.env`. + +# This file will be committed to version control, so make sure not to have any +# secrets in it. If you are cloning this repo, create a copy of this file named +# ".env" and populate it with your secrets. + +# When adding additional environment variables, the schema in "/src/env.mjs" +# should be updated accordingly. + +# Drizzle +# Get the Database URL from the "prisma" dropdown selector in PlanetScale. +# Change the query params at the end of the URL to "?ssl={"rejectUnauthorized":true}" +DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?ssl={"rejectUnauthorized":true}' + +# Next Auth +# You can generate a new secret on the command line with: +# openssl rand -base64 32 +# https://next-auth.js.org/configuration/options#secret +# NEXTAUTH_SECRET="" +NEXTAUTH_URL="http://localhost:3000" + +# Next Auth Discord Provider +DISCORD_CLIENT_ID="" +DISCORD_CLIENT_SECRET="" diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..79cb5118 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,36 @@ +/** @type {import("eslint").Linter.Config} */ +const config = { + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + }, + plugins: ["@typescript-eslint"], + extends: [ + "next/core-web-vitals", + "plugin:@typescript-eslint/recommended-type-checked", + "plugin:@typescript-eslint/stylistic-type-checked", + ], + rules: { + // These opinionated rules are enabled in stylistic-type-checked above. + // Feel free to reconfigure them to your own preference. + "@typescript-eslint/array-type": "off", + "@typescript-eslint/consistent-type-definitions": "off", + + "@typescript-eslint/consistent-type-imports": [ + "warn", + { + prefer: "type-imports", + fixStyle: "inline-type-imports", + }, + ], + "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], + "@typescript-eslint/no-misused-promises": [ + 2, + { + checksVoidReturn: { attributes: false }, + }, + ], + }, +}; + +module.exports = config; diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2971a0bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# database +/prisma/db.sqlite +/prisma/db.sqlite-journal + +# next.js +/.next/ +/out/ +next-env.d.ts + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables +.env +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..22073e71 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "spellright.addToSystemDictionary": true +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..956f2c96 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Kan + +The open source Trello alternative. diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..2e89ab8307ad3e53770e1ec5e42cd37ad37a0539 GIT binary patch literal 182885 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 literal 0 HcmV?d00001 diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 00000000..0f325756 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,12 @@ +import { type Config } from "drizzle-kit"; + +import { env } from "~/env.mjs"; + +export default { + schema: "./src/server/db/schema.ts", + driver: "mysql2", + dbCredentials: { + connectionString: env.DATABASE_URL, + }, + tablesFilter: ["kan_*"], +} satisfies Config; diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 00000000..0914d313 --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,10 @@ +/** + * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful + * for Docker builds. + */ +await import("./src/env.mjs"); + +/** @type {import("next").NextConfig} */ +const config = {}; + +export default config; diff --git a/package.json b/package.json new file mode 100644 index 00000000..4f412b0a --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "kan", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "next build", + "db:push": "dotenv drizzle-kit push:mysql", + "db:studio": "dotenv drizzle-kit studio", + "dev": "next dev", + "lint": "next lint", + "start": "next start" + }, + "dependencies": { + "@auth/drizzle-adapter": "^0.3.2", + "@planetscale/database": "^1.11.0", + "@t3-oss/env-nextjs": "^0.7.0", + "@tanstack/react-query": "^4.32.6", + "@trpc/client": "^10.37.1", + "@trpc/next": "^10.37.1", + "@trpc/react-query": "^10.37.1", + "@trpc/server": "^10.37.1", + "drizzle-orm": "^0.28.5", + "next": "^13.5.4", + "next-auth": "^4.23.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "superjson": "^1.13.1", + "zod": "^3.22.4" + }, + "devDependencies": { + "@types/eslint": "^8.44.2", + "@types/node": "^18.16.0", + "@types/react": "^18.2.20", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.3.0", + "@typescript-eslint/parser": "^6.3.0", + "autoprefixer": "^10.4.14", + "dotenv-cli": "^7.3.0", + "drizzle-kit": "^0.19.13", + "eslint": "^8.47.0", + "eslint-config-next": "^13.5.4", + "mysql2": "^3.6.1", + "postcss": "^8.4.27", + "prettier": "^3.0.0", + "prettier-plugin-tailwindcss": "^0.5.1", + "tailwindcss": "^3.3.3", + "typescript": "^5.1.6" + }, + "ct3aMetadata": { + "initVersion": "7.22.0" + } +} diff --git a/postcss.config.cjs b/postcss.config.cjs new file mode 100644 index 00000000..e305dd92 --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,8 @@ +const config = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; + +module.exports = config; diff --git a/prettier.config.mjs b/prettier.config.mjs new file mode 100644 index 00000000..2d2fa4c9 --- /dev/null +++ b/prettier.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').options} */ +const config = { + plugins: ["prettier-plugin-tailwindcss"], +}; + +export default config; diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..60c702aac13409c82c4040f8fee9603eb84aa10c GIT binary patch literal 15406 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 A#Q*>R literal 0 HcmV?d00001 diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..74f55343 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,11 @@ +import NextAuth from "next-auth"; + +import { authOptions } from "~/server/auth"; + +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +const handler = NextAuth(authOptions); +export { handler as GET, handler as POST }; + +export const runtime = "edge"; +export const preferredRegion = 'lhr1'; +export const dynamic = 'force-dynamic' diff --git a/src/app/api/trpc/[trpc]/route.ts b/src/app/api/trpc/[trpc]/route.ts new file mode 100644 index 00000000..9e63b2d6 --- /dev/null +++ b/src/app/api/trpc/[trpc]/route.ts @@ -0,0 +1,28 @@ +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; +import { type NextRequest } from "next/server"; + +import { env } from "~/env.mjs"; +import { appRouter } from "~/server/api/root"; +import { createTRPCContext } from "~/server/api/trpc"; + +const handler = (req: NextRequest) => + fetchRequestHandler({ + endpoint: "/api/trpc", + req, + router: appRouter, + createContext: () => createTRPCContext({ req }), + onError: + env.NODE_ENV === "development" + ? ({ path, error }) => { + console.error( + `❌ tRPC failed on ${path ?? ""}: ${error.message}` + ); + } + : undefined, + }); + +export { handler as GET, handler as POST }; + +export const runtime = "edge"; +export const preferredRegion = 'lhr1'; +export const dynamic = 'force-dynamic' diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 00000000..6081a715 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,31 @@ +import "~/styles/globals.css"; + +import { Plus_Jakarta_Sans } from "next/font/google"; +import { headers } from "next/headers"; + +import { TRPCReactProvider } from "~/trpc/react"; + +const jakarta = Plus_Jakarta_Sans({ + subsets: ["latin"], + variable: "--font-sans", +}); + +export const metadata = { + title: "Kan", + description: "The open source Trello alternative", + icons: [{ rel: "icon", url: "/favicon.ico" }], +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {children} + + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 00000000..2eae4325 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,7 @@ +export default function Home() { + return ( +
+

Kan

+
+ ); +} diff --git a/src/env.mjs b/src/env.mjs new file mode 100644 index 00000000..4b627b73 --- /dev/null +++ b/src/env.mjs @@ -0,0 +1,62 @@ +import { createEnv } from "@t3-oss/env-nextjs"; +import { z } from "zod"; + +export const env = createEnv({ + /** + * Specify your server-side environment variables schema here. This way you can ensure the app + * isn't built with invalid env vars. + */ + server: { + DATABASE_URL: z + .string() + .url() + .refine( + (str) => !str.includes("YOUR_MYSQL_URL_HERE"), + "You forgot to change the default URL", + ), + NODE_ENV: z + .enum(["development", "test", "production"]) + .default("development"), + NEXTAUTH_SECRET: + process.env.NODE_ENV === "production" + ? z.string() + : z.string().optional(), + NEXTAUTH_URL: z.preprocess( + // This makes Vercel deployments not fail if you don't set NEXTAUTH_URL + // Since NextAuth.js automatically uses the VERCEL_URL if present. + (str) => process.env.VERCEL_URL ?? str, + // VERCEL_URL doesn't include `https` so it cant be validated as a URL + process.env.VERCEL ? z.string() : z.string().url(), + ), + }, + + /** + * Specify your client-side environment variables schema here. This way you can ensure the app + * isn't built with invalid env vars. To expose them to the client, prefix them with + * `NEXT_PUBLIC_`. + */ + client: { + // NEXT_PUBLIC_CLIENTVAR: z.string(), + }, + + /** + * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. + * middlewares) or client-side so we need to destruct manually. + */ + runtimeEnv: { + DATABASE_URL: process.env.DATABASE_URL, + NODE_ENV: process.env.NODE_ENV, + NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, + NEXTAUTH_URL: process.env.NEXTAUTH_URL, + }, + /** + * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially + * useful for Docker builds. + */ + skipValidation: !!process.env.SKIP_ENV_VALIDATION, + /** + * Makes it so that empty strings are treated as undefined. + * `SOME_VAR: z.string()` and `SOME_VAR=''` will throw an error. + */ + emptyStringAsUndefined: true, +}); diff --git a/src/server/api/root.ts b/src/server/api/root.ts new file mode 100644 index 00000000..976ea23f --- /dev/null +++ b/src/server/api/root.ts @@ -0,0 +1,14 @@ +import { boardRouter } from "~/server/api/routers/board"; +import { createTRPCRouter } from "~/server/api/trpc"; + +/** + * This is the primary router for your server. + * + * All routers added in /api/routers should be manually added here. + */ +export const appRouter = createTRPCRouter({ + board: boardRouter, +}); + +// export type definition of API +export type AppRouter = typeof appRouter; diff --git a/src/server/api/routers/board.ts b/src/server/api/routers/board.ts new file mode 100644 index 00000000..f725fe41 --- /dev/null +++ b/src/server/api/routers/board.ts @@ -0,0 +1,14 @@ +import { + createTRPCRouter, + publicProcedure, +} from "~/server/api/trpc"; + +export const boardRouter = createTRPCRouter({ + all: publicProcedure.query(({ ctx }) => { + const userId = ctx.session?.user.id; + + if (!userId) return; + + return ctx.db.query.boards.findMany(); + }) +}); diff --git a/src/server/api/trpc.ts b/src/server/api/trpc.ts new file mode 100644 index 00000000..37cc16a5 --- /dev/null +++ b/src/server/api/trpc.ts @@ -0,0 +1,130 @@ +/** + * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: + * 1. You want to modify request context (see Part 1). + * 2. You want to create a new middleware or type of procedure (see Part 3). + * + * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will + * need to use are documented accordingly near the end. + */ + +import { initTRPC, TRPCError } from "@trpc/server"; +import { type NextRequest } from "next/server"; +import superjson from "superjson"; +import { ZodError } from "zod"; + +import { getServerAuthSession } from "~/server/auth"; +import { db } from "~/server/db"; + +/** + * 1. CONTEXT + * + * This section defines the "contexts" that are available in the backend API. + * + * These allow you to access things when processing a request, like the database, the session, etc. + */ + +interface CreateContextOptions { + headers: Headers; +} + +/** + * This helper generates the "internals" for a tRPC context. If you need to use it, you can export + * it from here. + * + * Examples of things you may need it for: + * - testing, so we don't have to mock Next.js' req/res + * - tRPC's `createSSGHelpers`, where we don't have req/res + * + * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts + */ +export const createInnerTRPCContext = async (opts: CreateContextOptions) => { + const session = await getServerAuthSession(); + + return { + session, + headers: opts.headers, + db, + }; +}; + +/** + * This is the actual context you will use in your router. It will be used to process every request + * that goes through your tRPC endpoint. + * + * @see https://trpc.io/docs/context + */ +export const createTRPCContext = async (opts: { req: NextRequest }) => { + // Fetch stuff that depends on the request + + return await createInnerTRPCContext({ + headers: opts.req.headers, + }); +}; + +/** + * 2. INITIALIZATION + * + * This is where the tRPC API is initialized, connecting the context and transformer. We also parse + * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation + * errors on the backend. + */ + +const t = initTRPC.context().create({ + transformer: superjson, + errorFormatter({ shape, error }) { + return { + ...shape, + data: { + ...shape.data, + zodError: + error.cause instanceof ZodError ? error.cause.flatten() : null, + }, + }; + }, +}); + +/** + * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) + * + * These are the pieces you use to build your tRPC API. You should import these a lot in the + * "/src/server/api/routers" directory. + */ + +/** + * This is how you create new routers and sub-routers in your tRPC API. + * + * @see https://trpc.io/docs/router + */ +export const createTRPCRouter = t.router; + +/** + * Public (unauthenticated) procedure + * + * This is the base piece you use to build new queries and mutations on your tRPC API. It does not + * guarantee that a user querying is authorized, but you can still access user session data if they + * are logged in. + */ +export const publicProcedure = t.procedure; + +/** Reusable middleware that enforces users are logged in before running the procedure. */ +const enforceUserIsAuthed = t.middleware(({ ctx, next }) => { + if (!ctx.session || !ctx.session.user) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + return next({ + ctx: { + // infers the `session` as non-nullable + session: { ...ctx.session, user: ctx.session.user }, + }, + }); +}); + +/** + * Protected (authenticated) procedure + * + * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies + * the session is valid and guarantees `ctx.session.user` is not null. + * + * @see https://trpc.io/docs/procedures + */ +export const protectedProcedure = t.procedure.use(enforceUserIsAuthed); diff --git a/src/server/auth.ts b/src/server/auth.ts new file mode 100644 index 00000000..98d7ba72 --- /dev/null +++ b/src/server/auth.ts @@ -0,0 +1,68 @@ +import { DrizzleAdapter } from "@auth/drizzle-adapter"; +import { + getServerSession, + type DefaultSession, + type NextAuthOptions, +} from "next-auth"; +import DiscordProvider from "next-auth/providers/discord"; + +import { env } from "~/env.mjs"; +import { db } from "~/server/db"; +import { mysqlTable } from "~/server/db/schema"; + +/** + * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` + * object and keep type safety. + * + * @see https://next-auth.js.org/getting-started/typescript#module-augmentation + */ +declare module "next-auth" { + interface Session extends DefaultSession { + user: { + id: string; + // ...other properties + // role: UserRole; + } & DefaultSession["user"]; + } + + // interface User { + // // ...other properties + // // role: UserRole; + // } +} + +/** + * Options for NextAuth.js used to configure adapters, providers, callbacks, etc. + * + * @see https://next-auth.js.org/configuration/options + */ +export const authOptions: NextAuthOptions = { + callbacks: { + session: ({ session, user }) => ({ + ...session, + user: { + ...session.user, + id: user.id, + }, + }), + }, + adapter: DrizzleAdapter(db, mysqlTable), + providers: [ + /** + * ...add more providers here. + * + * Most other providers require a bit more work than the Discord provider. For example, the + * GitHub provider requires you to add the `refresh_token_expires_in` field to the Account + * model. Refer to the NextAuth.js docs for the provider you want to use. Example: + * + * @see https://next-auth.js.org/providers/github + */ + ], +}; + +/** + * Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file. + * + * @see https://next-auth.js.org/configuration/nextjs + */ +export const getServerAuthSession = () => getServerSession(authOptions); diff --git a/src/server/db/index.ts b/src/server/db/index.ts new file mode 100644 index 00000000..2a0c8e8f --- /dev/null +++ b/src/server/db/index.ts @@ -0,0 +1,12 @@ +import { Client } from "@planetscale/database"; +import { drizzle } from "drizzle-orm/planetscale-serverless"; + +import { env } from "~/env.mjs"; +import * as schema from "./schema"; + +export const db = drizzle( + new Client({ + url: env.DATABASE_URL, + }).connection(), + { schema } +); diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts new file mode 100644 index 00000000..d2b50d31 --- /dev/null +++ b/src/server/db/schema.ts @@ -0,0 +1,105 @@ +import { relations, sql } from "drizzle-orm"; +import { + bigint, + index, + int, + mysqlTableCreator, + primaryKey, + text, + timestamp, + varchar, +} from "drizzle-orm/mysql-core"; +import { type AdapterAccount } from "next-auth/adapters"; + +/** + * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same + * database instance for multiple projects. + * + * @see https://orm.drizzle.team/docs/goodies#multi-project-schema + */ +export const mysqlTable = mysqlTableCreator((name) => `kan_${name}`); + +export const boards = mysqlTable( + "board", + { + id: bigint("id", { mode: "number" }).primaryKey().autoincrement(), + name: varchar("name", { length: 255 }), + createdBy: varchar("createdBy", { length: 255 }).notNull(), + createdAt: timestamp("created_at") + .default(sql`CURRENT_TIMESTAMP`) + .notNull(), + updatedAt: timestamp("updatedAt").onUpdateNow(), + }, +); + +export const users = mysqlTable("user", { + id: varchar("id", { length: 255 }).notNull().primaryKey(), + name: varchar("name", { length: 255 }), + email: varchar("email", { length: 255 }).notNull(), + emailVerified: timestamp("emailVerified", { + mode: "date", + fsp: 3, + }).default(sql`CURRENT_TIMESTAMP(3)`), + image: varchar("image", { length: 255 }), +}); + +export const usersRelations = relations(users, ({ many }) => ({ + accounts: many(accounts), +})); + +export const accounts = mysqlTable( + "account", + { + userId: varchar("userId", { length: 255 }).notNull(), + type: varchar("type", { length: 255 }) + .$type() + .notNull(), + provider: varchar("provider", { length: 255 }).notNull(), + providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(), + refresh_token: text("refresh_token"), + access_token: text("access_token"), + expires_at: int("expires_at"), + token_type: varchar("token_type", { length: 255 }), + scope: varchar("scope", { length: 255 }), + id_token: text("id_token"), + session_state: varchar("session_state", { length: 255 }), + }, + (account) => ({ + compoundKey: primaryKey(account.provider, account.providerAccountId), + userIdIdx: index("userId_idx").on(account.userId), + }) +); + +export const accountsRelations = relations(accounts, ({ one }) => ({ + user: one(users, { fields: [accounts.userId], references: [users.id] }), +})); + +export const sessions = mysqlTable( + "session", + { + sessionToken: varchar("sessionToken", { length: 255 }) + .notNull() + .primaryKey(), + userId: varchar("userId", { length: 255 }).notNull(), + expires: timestamp("expires", { mode: "date" }).notNull(), + }, + (session) => ({ + userIdIdx: index("userId_idx").on(session.userId), + }) +); + +export const sessionsRelations = relations(sessions, ({ one }) => ({ + user: one(users, { fields: [sessions.userId], references: [users.id] }), +})); + +export const verificationTokens = mysqlTable( + "verificationToken", + { + identifier: varchar("identifier", { length: 255 }).notNull(), + token: varchar("token", { length: 255 }).notNull(), + expires: timestamp("expires", { mode: "date" }).notNull(), + }, + (vt) => ({ + compoundKey: primaryKey(vt.identifier, vt.token), + }) +); diff --git a/src/styles/globals.css b/src/styles/globals.css new file mode 100644 index 00000000..b5c61c95 --- /dev/null +++ b/src/styles/globals.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/trpc/react.tsx b/src/trpc/react.tsx new file mode 100644 index 00000000..6429613d --- /dev/null +++ b/src/trpc/react.tsx @@ -0,0 +1,47 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { loggerLink, unstable_httpBatchStreamLink } from "@trpc/client"; +import { createTRPCReact } from "@trpc/react-query"; +import { useState } from "react"; + +import { type AppRouter } from "~/server/api/root"; +import { getUrl, transformer } from "./shared"; + +export const api = createTRPCReact(); + +export function TRPCReactProvider(props: { + children: React.ReactNode; + headers: Headers; +}) { + const [queryClient] = useState(() => new QueryClient()); + + const [trpcClient] = useState(() => + api.createClient({ + transformer, + links: [ + loggerLink({ + enabled: (op) => + process.env.NODE_ENV === "development" || + (op.direction === "down" && op.result instanceof Error), + }), + unstable_httpBatchStreamLink({ + url: getUrl(), + headers() { + const heads = new Map(props.headers); + heads.set("x-trpc-source", "react"); + return Object.fromEntries(heads); + }, + }), + ], + }) + ); + + return ( + + + {props.children} + + + ); +} diff --git a/src/trpc/server.ts b/src/trpc/server.ts new file mode 100644 index 00000000..6984f458 --- /dev/null +++ b/src/trpc/server.ts @@ -0,0 +1,28 @@ +import { + createTRPCProxyClient, + loggerLink, + unstable_httpBatchStreamLink, +} from "@trpc/client"; +import { headers } from "next/headers"; + +import { type AppRouter } from "~/server/api/root"; +import { getUrl, transformer } from "./shared"; + +export const api = createTRPCProxyClient({ + transformer, + links: [ + loggerLink({ + enabled: (op) => + process.env.NODE_ENV === "development" || + (op.direction === "down" && op.result instanceof Error), + }), + unstable_httpBatchStreamLink({ + url: getUrl(), + headers() { + const heads = new Map(headers()); + heads.set("x-trpc-source", "rsc"); + return Object.fromEntries(heads); + }, + }), + ], +}); diff --git a/src/trpc/shared.ts b/src/trpc/shared.ts new file mode 100644 index 00000000..46005045 --- /dev/null +++ b/src/trpc/shared.ts @@ -0,0 +1,30 @@ +import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server"; +import superjson from "superjson"; + +import { type AppRouter } from "~/server/api/root"; + +export const transformer = superjson; + +function getBaseUrl() { + if (typeof window !== "undefined") return ""; + if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; + return `http://localhost:${process.env.PORT ?? 3000}`; +} + +export function getUrl() { + return getBaseUrl() + "/api/trpc"; +} + +/** + * Inference helper for inputs. + * + * @example type HelloInput = RouterInputs['example']['hello'] + */ +export type RouterInputs = inferRouterInputs; + +/** + * Inference helper for outputs. + * + * @example type HelloOutput = RouterOutputs['example']['hello'] + */ +export type RouterOutputs = inferRouterOutputs; diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 00000000..f06488f9 --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,14 @@ +import { type Config } from "tailwindcss"; +import { fontFamily } from "tailwindcss/defaultTheme"; + +export default { + content: ["./src/**/*.tsx"], + theme: { + extend: { + fontFamily: { + sans: ["var(--font-sans)", ...fontFamily.sans], + }, + }, + }, + plugins: [], +} satisfies Config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..1dfa3a89 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "es2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "checkJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "noUncheckedIndexedAccess": true, + "baseUrl": ".", + "paths": { + "~/*": ["./src/*"] + }, + "plugins": [{ "name": "next" }] + }, + "include": [ + ".eslintrc.cjs", + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.cjs", + "**/*.mjs", + ".next/types/**/*.ts" + ], + "exclude": ["node_modules"] +}