Init project. Set up auth & db
This commit is contained in:
commit
217a0d3952
24
.env.example
Normal file
24
.env.example
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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.js"
|
||||||
|
# should be updated accordingly.
|
||||||
|
|
||||||
|
# Drizzle
|
||||||
|
DATABASE_URL="postgresql://postgres:password@localhost:5432/gibs_website"
|
||||||
|
|
||||||
|
# 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=""
|
61
.eslintrc.cjs
Normal file
61
.eslintrc.cjs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/** @type {import("eslint").Linter.Config} */
|
||||||
|
const config = {
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": true
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"drizzle"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"next/core-web-vitals",
|
||||||
|
"plugin:@typescript-eslint/recommended-type-checked",
|
||||||
|
"plugin:@typescript-eslint/stylistic-type-checked"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@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/require-await": "off",
|
||||||
|
"@typescript-eslint/no-misused-promises": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"checksVoidReturn": {
|
||||||
|
"attributes": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"drizzle/enforce-delete-with-where": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"drizzleObjectName": [
|
||||||
|
"db",
|
||||||
|
"ctx.db"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"drizzle/enforce-update-with-where": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"drizzleObjectName": [
|
||||||
|
"db",
|
||||||
|
"ctx.db"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = config;
|
46
.gitignore
vendored
Normal file
46
.gitignore
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# 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
|
||||||
|
db.sqlite
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# JetBrains Crap
|
||||||
|
.idea/
|
12
drizzle.config.ts
Normal file
12
drizzle.config.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { type Config } from "drizzle-kit";
|
||||||
|
|
||||||
|
import { env } from "~/env";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
schema: "./src/server/db/schema.ts",
|
||||||
|
dialect: "postgresql",
|
||||||
|
dbCredentials: {
|
||||||
|
url: env.DATABASE_URL,
|
||||||
|
},
|
||||||
|
tablesFilter: ["gibs_website_*"],
|
||||||
|
} satisfies Config;
|
10
next.config.js
Normal file
10
next.config.js
Normal file
@ -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.js");
|
||||||
|
|
||||||
|
/** @type {import("next").NextConfig} */
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
export default config;
|
49
package.json
Normal file
49
package.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "gibs_website",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "next build",
|
||||||
|
"db:generate": "drizzle-kit generate",
|
||||||
|
"db:migrate": "drizzle-kit migrate",
|
||||||
|
"db:push": "drizzle-kit push",
|
||||||
|
"db:studio": "drizzle-kit studio",
|
||||||
|
"dev": "next dev",
|
||||||
|
"lint": "next lint",
|
||||||
|
"start": "next start"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@auth/drizzle-adapter": "^1.1.0",
|
||||||
|
"@t3-oss/env-nextjs": "^0.10.1",
|
||||||
|
"drizzle-orm": "^0.30.10",
|
||||||
|
"geist": "^1.3.0",
|
||||||
|
"next": "^14.2.1",
|
||||||
|
"next-auth": "5.0.0-beta.19",
|
||||||
|
"postgres": "^3.4.4",
|
||||||
|
"react": "^18.3.0",
|
||||||
|
"react-dom": "^18.3.0",
|
||||||
|
"zod": "^3.23.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/eslint": "^8.56.2",
|
||||||
|
"@types/node": "^20.11.20",
|
||||||
|
"@types/react": "^18.2.57",
|
||||||
|
"@types/react-dom": "^18.2.19",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||||
|
"@typescript-eslint/parser": "^7.1.1",
|
||||||
|
"drizzle-kit": "^0.21.4",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-config-next": "^14.1.3",
|
||||||
|
"eslint-plugin-drizzle": "^0.2.3",
|
||||||
|
"postcss": "^8.4.34",
|
||||||
|
"prettier": "^3.2.5",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.5.14",
|
||||||
|
"tailwindcss": "^3.4.3",
|
||||||
|
"typescript": "^5.4.2"
|
||||||
|
},
|
||||||
|
"ct3aMetadata": {
|
||||||
|
"initVersion": "7.34.0"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@9.1.4"
|
||||||
|
}
|
4473
pnpm-lock.yaml
Normal file
4473
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
7
postcss.config.cjs
Normal file
7
postcss.config.cjs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
6
prettier.config.js
Normal file
6
prettier.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */
|
||||||
|
const config = {
|
||||||
|
plugins: ["prettier-plugin-tailwindcss"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
13
src/app/_components/auth/sign_in_gh.tsx
Normal file
13
src/app/_components/auth/sign_in_gh.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { signIn } from "~/auth"
|
||||||
|
|
||||||
|
export async function SignInGH() {
|
||||||
|
return (
|
||||||
|
<form className="w-full"
|
||||||
|
action={async () => {
|
||||||
|
"use server";
|
||||||
|
await signIn("github");
|
||||||
|
}}>
|
||||||
|
<button className="w-full" type="submit">Sign In With GitHub</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
13
src/app/_components/auth/sign_out.tsx
Normal file
13
src/app/_components/auth/sign_out.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { signOut } from "~/auth"
|
||||||
|
|
||||||
|
export function SignOut() {
|
||||||
|
return (
|
||||||
|
<form className="w-full"
|
||||||
|
action={async () => {
|
||||||
|
"use server"
|
||||||
|
await signOut()
|
||||||
|
}}>
|
||||||
|
<button type="submit" className="w-full">Sign Out</button>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
2
src/app/api/auth/[...nextauth]/route.ts
Normal file
2
src/app/api/auth/[...nextauth]/route.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { handlers } from "~/auth"
|
||||||
|
export const { GET, POST } = handlers
|
21
src/app/layout.tsx
Normal file
21
src/app/layout.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import "~/styles/globals.css";
|
||||||
|
|
||||||
|
import { GeistSans } from "geist/font/sans";
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: "Create T3 App",
|
||||||
|
description: "Generated by create-t3-app",
|
||||||
|
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="en" className={`${GeistSans.variable}`}>
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
37
src/app/page.tsx
Normal file
37
src/app/page.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function HomePage() {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white">
|
||||||
|
<div className="container flex flex-col items-center justify-center gap-12 px-4 py-16 ">
|
||||||
|
<h1 className="text-5xl font-extrabold tracking-tight text-white sm:text-[5rem]">
|
||||||
|
Create <span className="text-[hsl(280,100%,70%)]">T3</span> App
|
||||||
|
</h1>
|
||||||
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-8">
|
||||||
|
<Link
|
||||||
|
className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20"
|
||||||
|
href="https://create.t3.gg/en/usage/first-steps"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<h3 className="text-2xl font-bold">First Steps →</h3>
|
||||||
|
<div className="text-lg">
|
||||||
|
Just the basics - Everything you need to know to set up your
|
||||||
|
database and authentication.
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20"
|
||||||
|
href="https://create.t3.gg/en/introduction"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<h3 className="text-2xl font-bold">Documentation →</h3>
|
||||||
|
<div className="text-lg">
|
||||||
|
Learn more about Create T3 App, the libraries it uses, and how to
|
||||||
|
deploy it.
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
6
src/auth.ts
Normal file
6
src/auth.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import NextAuth from "next-auth"
|
||||||
|
import GitHub from "next-auth/providers/github"
|
||||||
|
|
||||||
|
export const { handlers, auth, signIn, signOut } = NextAuth({
|
||||||
|
providers: [ GitHub ],
|
||||||
|
})
|
52
src/env.js
Normal file
52
src/env.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
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(),
|
||||||
|
NODE_ENV: z
|
||||||
|
.enum(["development", "test", "production"])
|
||||||
|
.default("development"),
|
||||||
|
NEXTAUTH_SECRET:
|
||||||
|
process.env.NODE_ENV === "production"
|
||||||
|
? z.string()
|
||||||
|
: z.string().optional(),
|
||||||
|
AUTH_GITHUB_ID: z.string(),
|
||||||
|
AUTH_GITHUB_SECRET: z.string(),
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
AUTH_GITHUB_ID: process.env.AUTH_GITHUB_ID,
|
||||||
|
AUTH_GITHUB_SECRET: process.env.AUTH_GITHUB_SECRET,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
});
|
83
src/server/auth.ts
Normal file
83
src/server/auth.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { DrizzleAdapter } from "@auth/drizzle-adapter";
|
||||||
|
import {
|
||||||
|
getServerSession,
|
||||||
|
type DefaultSession,
|
||||||
|
type NextAuthOptions,
|
||||||
|
} from "next-auth";
|
||||||
|
import { type Adapter } from "next-auth/adapters";
|
||||||
|
import DiscordProvider from "next-auth/providers/discord";
|
||||||
|
|
||||||
|
import { env } from "~/env";
|
||||||
|
import { db } from "~/server/db";
|
||||||
|
import {
|
||||||
|
accounts,
|
||||||
|
sessions,
|
||||||
|
users,
|
||||||
|
verificationTokens,
|
||||||
|
} 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, {
|
||||||
|
usersTable: users,
|
||||||
|
accountsTable: accounts,
|
||||||
|
sessionsTable: sessions,
|
||||||
|
verificationTokensTable: verificationTokens,
|
||||||
|
}) as Adapter,
|
||||||
|
providers: [
|
||||||
|
DiscordProvider({
|
||||||
|
clientId: env.DISCORD_CLIENT_ID,
|
||||||
|
clientSecret: env.DISCORD_CLIENT_SECRET,
|
||||||
|
}),
|
||||||
|
/**
|
||||||
|
* ...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);
|
18
src/server/db/index.ts
Normal file
18
src/server/db/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/postgres-js";
|
||||||
|
import postgres from "postgres";
|
||||||
|
|
||||||
|
import { env } from "~/env";
|
||||||
|
import * as schema from "./schema";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache the database connection in development. This avoids creating a new connection on every HMR
|
||||||
|
* update.
|
||||||
|
*/
|
||||||
|
const globalForDb = globalThis as unknown as {
|
||||||
|
conn: postgres.Sql | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const conn = globalForDb.conn ?? postgres(env.DATABASE_URL);
|
||||||
|
if (env.NODE_ENV !== "production") globalForDb.conn = conn;
|
||||||
|
|
||||||
|
export const db = drizzle(conn, { schema });
|
123
src/server/db/schema.ts
Normal file
123
src/server/db/schema.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { relations, sql } from "drizzle-orm";
|
||||||
|
import {
|
||||||
|
index,
|
||||||
|
integer,
|
||||||
|
pgTableCreator,
|
||||||
|
primaryKey,
|
||||||
|
serial,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
varchar,
|
||||||
|
} from "drizzle-orm/pg-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 createTable = pgTableCreator((name) => `gibs_website_${name}`);
|
||||||
|
|
||||||
|
export const posts = createTable(
|
||||||
|
"post",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: varchar("name", { length: 256 }),
|
||||||
|
createdById: varchar("createdById", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.default(sql`CURRENT_TIMESTAMP`)
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: timestamp("updatedAt", { withTimezone: true }),
|
||||||
|
},
|
||||||
|
(example) => ({
|
||||||
|
createdByIdIdx: index("createdById_idx").on(example.createdById),
|
||||||
|
nameIndex: index("name_idx").on(example.name),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const users = createTable("user", {
|
||||||
|
id: varchar("id", { length: 255 }).notNull().primaryKey(),
|
||||||
|
name: varchar("name", { length: 255 }),
|
||||||
|
email: varchar("email", { length: 255 }).notNull(),
|
||||||
|
emailVerified: timestamp("emailVerified", {
|
||||||
|
mode: "date",
|
||||||
|
withTimezone: true,
|
||||||
|
}).default(sql`CURRENT_TIMESTAMP`),
|
||||||
|
image: varchar("image", { length: 255 }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const usersRelations = relations(users, ({ many }) => ({
|
||||||
|
accounts: many(accounts),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const accounts = createTable(
|
||||||
|
"account",
|
||||||
|
{
|
||||||
|
userId: varchar("userId", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id),
|
||||||
|
type: varchar("type", { length: 255 })
|
||||||
|
.$type<AdapterAccount["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: integer("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({
|
||||||
|
columns: [account.provider, account.providerAccountId],
|
||||||
|
}),
|
||||||
|
userIdIdx: index("account_userId_idx").on(account.userId),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const accountsRelations = relations(accounts, ({ one }) => ({
|
||||||
|
user: one(users, { fields: [accounts.userId], references: [users.id] }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const sessions = createTable(
|
||||||
|
"session",
|
||||||
|
{
|
||||||
|
sessionToken: varchar("sessionToken", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.primaryKey(),
|
||||||
|
userId: varchar("userId", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id),
|
||||||
|
expires: timestamp("expires", {
|
||||||
|
mode: "date",
|
||||||
|
withTimezone: true,
|
||||||
|
}).notNull(),
|
||||||
|
},
|
||||||
|
(session) => ({
|
||||||
|
userIdIdx: index("session_userId_idx").on(session.userId),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const sessionsRelations = relations(sessions, ({ one }) => ({
|
||||||
|
user: one(users, { fields: [sessions.userId], references: [users.id] }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const verificationTokens = createTable(
|
||||||
|
"verificationToken",
|
||||||
|
{
|
||||||
|
identifier: varchar("identifier", { length: 255 }).notNull(),
|
||||||
|
token: varchar("token", { length: 255 }).notNull(),
|
||||||
|
expires: timestamp("expires", {
|
||||||
|
mode: "date",
|
||||||
|
withTimezone: true,
|
||||||
|
}).notNull(),
|
||||||
|
},
|
||||||
|
(vt) => ({
|
||||||
|
compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
|
||||||
|
})
|
||||||
|
);
|
3
src/styles/globals.css
Normal file
3
src/styles/globals.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
14
tailwind.config.ts
Normal file
14
tailwind.config.ts
Normal file
@ -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-geist-sans)", ...fontFamily.sans],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
} satisfies Config;
|
42
tsconfig.json
Normal file
42
tsconfig.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Base Options: */
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"target": "es2022",
|
||||||
|
"allowJs": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"isolatedModules": true,
|
||||||
|
|
||||||
|
/* Strictness */
|
||||||
|
"strict": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"checkJs": true,
|
||||||
|
|
||||||
|
/* Bundled projects */
|
||||||
|
"lib": ["dom", "dom.iterable", "ES2022"],
|
||||||
|
"noEmit": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"jsx": "preserve",
|
||||||
|
"plugins": [{ "name": "next" }],
|
||||||
|
"incremental": true,
|
||||||
|
|
||||||
|
/* Path Aliases */
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
".eslintrc.cjs",
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"**/*.cjs",
|
||||||
|
"**/*.js",
|
||||||
|
".next/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user