Skip to content

Commit 6c5ca25

Browse files
authored
Update all dependencies. (vercel#48)
2 parents 7477bca + a6ac1d3 commit 6c5ca25

32 files changed

+5658
-1821
lines changed

.env.example

+1-5
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22
POSTGRES_URL=
33
POSTGRES_PRISMA_URL=
44
POSTGRES_URL_NON_POOLING=
5-
POSTGRES_USER=
6-
POSTGRES_HOST=
7-
POSTGRES_PASSWORD=
8-
POSTGRES_DATABASE=
95

106
# Generate one here: https://generate-secret.vercel.app/32 (only required for localhost)
11-
NEXTAUTH_SECRET=
7+
AUTH_SECRET=

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ yarn-error.log*
3434

3535
# vercel
3636
.vercel
37+
.env*.local

README.md

+4-15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
1-
<p align="center">
2-
<a href="https://nextjs-postgres-auth.vercel.app/">
3-
<img src="/public/logo.png" height="96">
4-
<h3 align="center">Next.js Prisma PostgreSQL Auth Starter</h3>
5-
</a>
6-
</p>
1+
# Next.js + PostgreSQL Auth Starter
72

8-
<p align="center">
9-
This is a <a href="https://nextjs.org/">Next.js</a> starter kit that uses <a href="https://next-auth.js.org/">Next-Auth</a> for simple email + password login<br/>
10-
<a href="https://www.prisma.io/">Prisma</a> as the ORM, and a <a href="https://vercel.com/postgres">Vercel Postgres</a> database to persist the data.</p>
11-
12-
<br/>
3+
This is a [Next.js](https://nextjs.org/) starter kit that uses [NextAuth.js](https://next-auth.js.org/) for simple email + password login, [Drizzle](https://orm.drizzle.team) as the ORM, and a [Neon Postgres](https://vercel.com/postgres) database to persist the data.
134

145
## Deploy Your Own
156

167
You can clone & deploy it to Vercel with one click:
178

18-
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=Next.js%20Prisma%20PostgreSQL%20Auth%20Starter&demo-description=Simple%20Next.js%2013%20starter%20kit%20that%20uses%20Next-Auth%20for%20auth%20and%20Prisma%20PostgreSQL%20as%20a%20database.&demo-url=https%3A%2F%2Fnextjs-postgres-auth.vercel.app%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F7rsVQ1ZBSiWe9JGO6FUeZZ%2F210cba91036ca912b2770e0bd5d6cc5d%2Fthumbnail.png&project-name=Next.js%%20Prisma%20PostgreSQL%20Auth%20Starter&repository-name=nextjs-postgres-auth-starter&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnextjs-postgres-auth-starter&from=templates&skippable-integrations=1&env=NEXTAUTH_SECRET&envDescription=Generate%20a%20random%20secret%3A&envLink=https://generate-secret.vercel.app/&stores=%5B%7B"type"%3A"postgres"%7D%5D)
9+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=Next.js%20Prisma%20PostgreSQL%20Auth%20Starter&demo-description=Simple%20Next.js%2013%20starter%20kit%20that%20uses%20Next-Auth%20for%20auth%20and%20Prisma%20PostgreSQL%20as%20a%20database.&demo-url=https%3A%2F%2Fnextjs-postgres-auth.vercel.app%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F7rsVQ1ZBSiWe9JGO6FUeZZ%2F210cba91036ca912b2770e0bd5d6cc5d%2Fthumbnail.png&project-name=Next.js%%20Prisma%20PostgreSQL%20Auth%20Starter&repository-name=nextjs-postgres-auth-starter&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnextjs-postgres-auth-starter&from=templates&skippable-integrations=1&env=AUTH_SECRET&envDescription=Generate%20a%20random%20secret%3A&envLink=https://generate-secret.vercel.app/&stores=%5B%7B"type"%3A"postgres"%7D%5D)
1910

2011
## Developing Locally
2112

@@ -30,9 +21,7 @@ npx create-next-app nextjs-typescript-starter --example "https://github.com/verc
3021
First, run the development server:
3122

3223
```bash
33-
npm run dev
34-
# or
35-
yarn dev
24+
pnpm dev
3625
```
3726

3827
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

app/api/auth/[...nextauth]/route.ts

+1-35
Original file line numberDiff line numberDiff line change
@@ -1,35 +1 @@
1-
import NextAuth, { type NextAuthOptions } from "next-auth";
2-
import CredentialsProvider from "next-auth/providers/credentials";
3-
import prisma from "@/lib/prisma";
4-
import { compare } from "bcrypt";
5-
6-
export const authOptions: NextAuthOptions = {
7-
providers: [
8-
CredentialsProvider({
9-
credentials: {
10-
email: { label: "Email", type: "email" },
11-
password: { label: "Password", type: "password" }
12-
},
13-
async authorize(credentials) {
14-
const { email, password } = credentials ?? {}
15-
if (!email || !password) {
16-
throw new Error("Missing username or password");
17-
}
18-
const user = await prisma.user.findUnique({
19-
where: {
20-
email,
21-
},
22-
});
23-
// if user doesn't exist or password doesn't match
24-
if (!user || !(await compare(password, user.password))) {
25-
throw new Error("Invalid username or password");
26-
}
27-
return user;
28-
},
29-
}),
30-
],
31-
};
32-
33-
const handler = NextAuth(authOptions);
34-
35-
export { handler as GET, handler as POST };
1+
export { GET, POST } from 'app/auth';

app/api/auth/register/route.ts

-24
This file was deleted.

app/auth.config.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { NextAuthConfig } from 'next-auth';
2+
3+
export const authConfig = {
4+
pages: {
5+
signIn: '/login',
6+
},
7+
providers: [
8+
// added later in auth.ts since it requires bcrypt which is only compatible with Node.js
9+
// while this file is also used in non-Node.js environments
10+
],
11+
callbacks: {
12+
authorized({ auth, request: { nextUrl } }) {
13+
let isLoggedIn = !!auth?.user;
14+
let isOnDashboard = nextUrl.pathname.startsWith('/protected');
15+
16+
if (isOnDashboard) {
17+
if (isLoggedIn) return true;
18+
return false; // Redirect unauthenticated users to login page
19+
} else if (isLoggedIn) {
20+
return Response.redirect(new URL('/protected', nextUrl));
21+
}
22+
23+
return true;
24+
},
25+
},
26+
} satisfies NextAuthConfig;

app/auth.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import NextAuth from 'next-auth';
2+
import Credentials from 'next-auth/providers/credentials';
3+
import { compare } from 'bcrypt-ts';
4+
import { getUser } from 'app/db';
5+
import { authConfig } from 'app/auth.config';
6+
7+
export const {
8+
handlers: { GET, POST },
9+
auth,
10+
signIn,
11+
signOut,
12+
} = NextAuth({
13+
...authConfig,
14+
providers: [
15+
Credentials({
16+
async authorize({ email, password }: any) {
17+
let user = await getUser(email);
18+
if (user.length === 0) return null;
19+
let passwordsMatch = await compare(password, user[0].password!);
20+
if (passwordsMatch) return user[0] as any;
21+
},
22+
}),
23+
],
24+
});

app/db.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { drizzle } from 'drizzle-orm/postgres-js';
2+
import { pgTable, serial, varchar } from 'drizzle-orm/pg-core';
3+
import { eq } from 'drizzle-orm';
4+
import postgres from 'postgres';
5+
import { genSaltSync, hashSync } from 'bcrypt-ts';
6+
7+
// Optionally, if not using email/pass login, you can
8+
// use the Drizzle adapter for Auth.js / NextAuth
9+
// https://authjs.dev/reference/adapter/drizzle
10+
let client = postgres(`${process.env.POSTGRES_URL!}?sslmode=require`);
11+
let db = drizzle(client);
12+
13+
let users = pgTable('User', {
14+
id: serial('id').primaryKey(),
15+
email: varchar('email', { length: 64 }),
16+
password: varchar('password', { length: 64 }),
17+
});
18+
19+
export async function getUser(email: string) {
20+
return await db.select().from(users).where(eq(users.email, email));
21+
}
22+
23+
export async function createUser(email: string, password: string) {
24+
let salt = genSaltSync(10);
25+
let hash = hashSync(password, salt);
26+
27+
return await db.insert(users).values({ email, password: hash });
28+
}

app/form.tsx

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
export function Form({
2+
action,
3+
children,
4+
}: {
5+
action: any;
6+
children: React.ReactNode;
7+
}) {
8+
return (
9+
<form
10+
action={action}
11+
className="flex flex-col space-y-4 bg-gray-50 px-4 py-8 sm:px-16"
12+
>
13+
<div>
14+
<label
15+
htmlFor="email"
16+
className="block text-xs text-gray-600 uppercase"
17+
>
18+
Email Address
19+
</label>
20+
<input
21+
id="email"
22+
name="email"
23+
type="email"
24+
placeholder="[email protected]"
25+
autoComplete="email"
26+
required
27+
className="mt-1 block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
28+
/>
29+
</div>
30+
<div>
31+
<label
32+
htmlFor="password"
33+
className="block text-xs text-gray-600 uppercase"
34+
>
35+
Password
36+
</label>
37+
<input
38+
id="password"
39+
name="password"
40+
type="password"
41+
required
42+
className="mt-1 block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
43+
/>
44+
</div>
45+
{children}
46+
</form>
47+
);
48+
}

styles/globals.css app/globals.css

File renamed without changes.

app/layout.tsx

+10-27
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,30 @@
1-
// These styles apply to every route in the application
2-
import "@/styles/globals.css";
3-
import { Metadata } from "next";
4-
import { Inter } from "next/font/google";
5-
import { Toaster } from "react-hot-toast";
6-
import AuthStatus from "@/components/auth-status";
7-
import { Suspense } from "react";
1+
import './globals.css';
82

9-
const inter = Inter({
10-
variable: "--font-inter",
11-
subsets: ["latin"],
12-
});
3+
import { GeistSans } from 'geist/font/sans';
134

14-
const title = "Next.js Prisma Postgres Auth Starter";
15-
const description =
16-
"This is a Next.js starter kit that uses Next-Auth for simple email + password login and a Postgres database to persist the data.";
5+
let title = 'Next.js + Postgres Auth Starter';
6+
let description =
7+
'This is a Next.js starter kit that uses NextAuth.js for simple email + password login and a Postgres database to persist the data.';
178

18-
export const metadata: Metadata = {
9+
export const metadata = {
1910
title,
2011
description,
2112
twitter: {
22-
card: "summary_large_image",
13+
card: 'summary_large_image',
2314
title,
2415
description,
2516
},
26-
metadataBase: new URL("https://nextjs-postgres-auth.vercel.app"),
27-
themeColor: "#FFF",
17+
metadataBase: new URL('https://nextjs-postgres-auth.vercel.app'),
2818
};
2919

30-
export default async function RootLayout({
20+
export default function RootLayout({
3121
children,
3222
}: {
3323
children: React.ReactNode;
3424
}) {
3525
return (
3626
<html lang="en">
37-
<body className={inter.variable}>
38-
<Toaster />
39-
<Suspense fallback="Loading...">
40-
{/* @ts-expect-error Async Server Component */}
41-
<AuthStatus />
42-
</Suspense>
43-
{children}
44-
</body>
27+
<body className={GeistSans.variable}>{children}</body>
4528
</html>
4629
);
4730
}

app/login/page.tsx

+23-14
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,37 @@
1-
import Image from "next/image";
2-
import Form from "@/components/form";
3-
import Link from "next/link";
1+
import Link from 'next/link';
2+
import { Form } from 'app/form';
3+
import { signIn } from 'app/auth';
4+
import { SubmitButton } from 'app/submit-button';
45

56
export default function Login() {
67
return (
78
<div className="flex h-screen w-screen items-center justify-center bg-gray-50">
89
<div className="z-10 w-full max-w-md overflow-hidden rounded-2xl border border-gray-100 shadow-xl">
910
<div className="flex flex-col items-center justify-center space-y-3 border-b border-gray-200 bg-white px-4 py-6 pt-8 text-center sm:px-16">
10-
<Link href="/">
11-
<Image
12-
src="/logo.png"
13-
priority
14-
alt="Logo"
15-
className="h-10 w-10 rounded-full"
16-
width={20}
17-
height={20}
18-
/>
19-
</Link>
2011
<h3 className="text-xl font-semibold">Sign In</h3>
2112
<p className="text-sm text-gray-500">
2213
Use your email and password to sign in
2314
</p>
2415
</div>
25-
<Form type="login" />
16+
<Form
17+
action={async (formData: FormData) => {
18+
'use server';
19+
await signIn('credentials', {
20+
redirectTo: '/protected',
21+
email: formData.get('email') as string,
22+
password: formData.get('password') as string,
23+
});
24+
}}
25+
>
26+
<SubmitButton>Sign in</SubmitButton>
27+
<p className="text-center text-sm text-gray-600">
28+
{"Don't have an account? "}
29+
<Link href="/register" className="font-semibold text-gray-800">
30+
Sign up
31+
</Link>
32+
{' for free.'}
33+
</p>
34+
</Form>
2635
</div>
2736
</div>
2837
);

app/opengraph-image.png

-324 KB
Binary file not shown.

0 commit comments

Comments
 (0)