Skip to content

Commit

Permalink
feat: enables app, next-auth schema
Browse files Browse the repository at this point in the history
* feat: enables app directory now that is stable, better documentation, updates dependencies
  • Loading branch information
en3sis authored May 26, 2023
1 parent f42266c commit 432fbf2
Show file tree
Hide file tree
Showing 18 changed files with 756 additions and 515 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ The `pages/api` directory is mapped to `/api/*`. Files in this directory are tre

We're using Supabase with Next-Auth for authentication and authorization.

For the integration, check and following the steps in Next-Auth documentation: https://next-auth.js.org/adapters/supabase
> NOTE: You can import the schema from `supabase/schema.sql` to your Supabase instance.
For the integration, check and following the steps in Next-Auth documentation: https://authjs.dev/reference/adapter/supabase

After setting up your Supabase instance and configuration, generating types for your database is an important step towards improving your development workflow. By generating types, you can ensure type safety and catch potential errors earlier in the development process.

Expand Down
45 changes: 45 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Menu from '@/components/menu'
import { Metadata } from 'next'
import { Session } from 'next-auth'
import { headers } from 'next/dist/client/components/headers'
import ClientSessionProvider from '../components/auth/session-provider'
import '../global.css'

export const metadata: Metadata = {
title: '🍃 Nextwind',
description:
'Introducing a powerful and efficient full- stack web development starter project built with TypeScript, Next.JS, Supabase, PostgreSQL, TailwindCSS, and Zustand!',
}

export interface DashboardLayoutProps {
children: React.ReactNode
}

export async function getSession(cookie: string): Promise<Session | null> {
const response = await fetch(`${process.env.NEXTAUTH_URL}/api/auth/session`, {
headers: { cookie },
})

if (!response?.ok) {
return null
}

const session = await response.json()
return Object.keys(session).length > 0 ? session : null
}

export default async function Layout({ children }: { children: React.ReactNode; props?: any }) {
const session = await getSession(headers().get('cookie') ?? '')

return (
<html>
<head />
<body className='bg-[#081113] text-gray-300'>
<ClientSessionProvider session={session}>
<Menu />
<div className='container grid mx-auto max-w-5xl h-screen'>{children}</div>
</ClientSessionProvider>
</body>
</html>
)
}
4 changes: 2 additions & 2 deletions pages/index.tsx → app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default function IndexPage() {
export default function Page() {
return (
<div className='flex flex-col gap-2'>
<div className='flex flex-col gap-4 py-4'>
<h1 className='text-2xl'>Welcome!</h1>
<p>
TypeScript base project with Next.JS as React Framework, Supabase with PostgreSQL as DataBase, Tailwind CSS as
Expand Down
13 changes: 13 additions & 0 deletions components/auth/auth-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use client'

import { Session } from 'next-auth'
import { SessionProvider } from 'next-auth/react'

export interface AuthContextProps {
children: React.ReactNode
session: Session
}

export default function AuthContext({ children }: AuthContextProps) {
return <SessionProvider>{children}</SessionProvider>
}
7 changes: 7 additions & 0 deletions components/auth/session-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use client'

import { SessionProvider, SessionProviderProps } from 'next-auth/react'

export default function ClientSessionProvider(props: SessionProviderProps) {
return <SessionProvider {...props} />
}
12 changes: 0 additions & 12 deletions components/layout.tsx

This file was deleted.

8 changes: 5 additions & 3 deletions components/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import User from './user'

export const Menu = () => {
return (
<div className='flex flex-row justify-between text-white bg-indigo-500 border-b-4 border-indigo-600 p-4'>
<div>🍃 Nextwind</div>
<User />
<div className='text-white p-4 border-b border-white border-opacity-10 border-dotted'>
<div className='container flex flex-row justify-between mx-auto max-w-5xl'>
<span>🍃 Nextwind</span>
<User />
</div>
</div>
)
}
Expand Down
7 changes: 6 additions & 1 deletion components/user.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable @next/next/no-img-element */
'use client'
import { signIn, signOut, useSession } from 'next-auth/react'

const User = () => {
Expand All @@ -18,7 +19,11 @@ const User = () => {
{status === 'authenticated' && (
<>
<p className='text-sm font-medium'>Welcome, {data?.user.name}</p>
<img onClick={() => signOut()} className='w-8 h-8 rounded-full border-2' src={data?.user.image} alt='' />
{data?.user.image ? (
<img onClick={() => signOut()} className='w-8 h-8 rounded-full border-2' src={data.user?.image} alt='' />
) : (
<div className='w-8 h-8 rounded-full border-2' />
)}
</>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion libs/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createClient } from '@supabase/supabase-js'
import { Database } from 'types/database.types'

export const supabase = async (jwt_token: string) =>
createClient<Database>(process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, {
createClient<Database>(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE!, {
global: {
headers: {
Authorization: `Bearer ${jwt_token}`,
Expand Down
1 change: 1 addition & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
reactStrictMode: true,
experimental: { appDir: false }, // It stills buggy, we need to wait for the next release.
experimental: { appDir: true }, // It stills buggy, we need to wait for the next release.
output: 'standalone',
images: {
domains: ['upload.wikimedia.org'],
Expand Down
38 changes: 19 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"author": "en3sis",
"scripts": {
"dev": "NODE_OPTIONS='--inspect' next dev",
"dev-turbo": "NODE_OPTIONS='--inspect' next dev --turbo",
"dev:turbo": "NODE_OPTIONS='--inspect' next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint",
Expand All @@ -15,30 +15,30 @@
},
"dependencies": {
"@next-auth/supabase-adapter": "^0.2.1",
"@supabase/supabase-js": "^2.8.0",
"@types/jsonwebtoken": "^9.0.1",
"eslint-config-prettier": "^8.6.0",
"framer-motion": "^9.0.7",
"@supabase/supabase-js": "^2.22.0",
"@types/jsonwebtoken": "^9.0.2",
"eslint-config-prettier": "^8.8.0",
"framer-motion": "^10.12.16",
"husky": "^8.0.3",
"jsonwebtoken": "^9.0.0",
"next": "^13.1.6",
"next-auth": "^4.20.1",
"next": "^13.4.3",
"next-auth": "^4.22.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zustand": "^4.3.3"
"zustand": "^4.3.8"
},
"devDependencies": {
"@types/node": "^18.14.0",
"@types/react": "^18.0.28",
"@typescript-eslint/eslint-plugin": "^5.53.0",
"@typescript-eslint/parser": "^5.53.0",
"autoprefixer": "^10.4.12",
"eslint": "8.34.0",
"eslint-config-next": "^13.1.6",
"postcss": "^8.4.18",
"prettier": "^2.8.4",
"@types/node": "^20.2.3",
"@types/react": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^5.59.7",
"@typescript-eslint/parser": "^5.59.7",
"autoprefixer": "^10.4.14",
"eslint": "8.41.0",
"eslint-config-next": "^13.4.3",
"postcss": "^8.4.23",
"prettier": "^2.8.8",
"prettier-plugin-organize-imports": "^3.2.2",
"tailwindcss": "^3.2.7",
"typescript": "^4.9.5"
"tailwindcss": "^3.3.2",
"typescript": "^5.0.4"
}
}
23 changes: 0 additions & 23 deletions pages/_app.tsx

This file was deleted.

22 changes: 18 additions & 4 deletions pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import { SupabaseAdapter } from '@next-auth/supabase-adapter'
import { createClient } from '@supabase/supabase-js'
import jwt from 'jsonwebtoken'
import NextAuth from 'next-auth'
import GitHubProvider from 'next-auth/providers/github'

const supabaseServer = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE!, {
db: { schema: 'next_auth' },
})

// For more information on each option (and a full list of options) go to
// https://next-auth.js.org/configuration/options
export default NextAuth({
// https://next-auth.js.org/configuration/providers
secret: process.env.NEXT_AUTH_SECRET,
providers: [
GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
}),
],
adapter: SupabaseAdapter({
url: process.env.NEXT_PUBLIC_SUPABASE_URL,
secret: process.env.SUPABASE_SERVICE_ROLE,
url: process.env.NEXT_PUBLIC_SUPABASE_URL!,
secret: process.env.SUPABASE_SERVICE_ROLE!,
}),
callbacks: {
async session({ session, user }) {
Expand All @@ -29,7 +34,16 @@ export default NextAuth({
email: user.email,
role: 'authenticated',
}

// Get the user's gh access token from Supabase
const { data, error } = await supabaseServer.from('accounts').select('*').eq('userId', user.id).single()

if (error) {
console.error(error)
}

session.supabaseAccessToken = jwt.sign(payload, signingSecret)
session.ghAccessToken = data?.access_token
}
return session
},
Expand Down
108 changes: 108 additions & 0 deletions supabase/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
create schema if not exists "next_auth";

create table "next_auth"."accounts" (
"id" uuid not null default uuid_generate_v4(),
"type" text not null,
"provider" text not null,
"providerAccountId" text not null,
"refresh_token" text,
"access_token" text,
"expires_at" bigint,
"token_type" text,
"scope" text,
"id_token" text,
"session_state" text,
"oauth_token_secret" text,
"oauth_token" text,
"userId" uuid,
"refresh_token_expires_in" bigint
);


alter table "next_auth"."accounts" enable row level security;

create table "next_auth"."sessions" (
"id" uuid not null default uuid_generate_v4(),
"expires" timestamp with time zone not null,
"sessionToken" text not null,
"userId" uuid
);


alter table "next_auth"."sessions" enable row level security;

create table "next_auth"."users" (
"id" uuid not null default uuid_generate_v4(),
"name" text,
"email" text,
"emailVerified" timestamp with time zone,
"image" text,
"refresh_token_expires_in" timestamp without time zone
);


alter table "next_auth"."users" enable row level security;

create table "next_auth"."verification_tokens" (
"identifier" text,
"token" text not null,
"expires" timestamp with time zone not null
);


alter table "next_auth"."verification_tokens" enable row level security;

CREATE UNIQUE INDEX accounts_pkey ON next_auth.accounts USING btree (id);

CREATE UNIQUE INDEX email_unique ON next_auth.users USING btree (email);

CREATE UNIQUE INDEX provider_unique ON next_auth.accounts USING btree (provider, "providerAccountId");

CREATE UNIQUE INDEX sessions_pkey ON next_auth.sessions USING btree (id);

CREATE UNIQUE INDEX sessiontoken_unique ON next_auth.sessions USING btree ("sessionToken");

CREATE UNIQUE INDEX token_identifier_unique ON next_auth.verification_tokens USING btree (token, identifier);

CREATE UNIQUE INDEX users_pkey ON next_auth.users USING btree (id);

CREATE UNIQUE INDEX verification_tokens_pkey ON next_auth.verification_tokens USING btree (token);

alter table "next_auth"."accounts" add constraint "accounts_pkey" PRIMARY KEY using index "accounts_pkey";

alter table "next_auth"."sessions" add constraint "sessions_pkey" PRIMARY KEY using index "sessions_pkey";

alter table "next_auth"."users" add constraint "users_pkey" PRIMARY KEY using index "users_pkey";

alter table "next_auth"."verification_tokens" add constraint "verification_tokens_pkey" PRIMARY KEY using index "verification_tokens_pkey";

alter table "next_auth"."accounts" add constraint "accounts_userId_fkey" FOREIGN KEY ("userId") REFERENCES next_auth.users(id) ON DELETE CASCADE not valid;

alter table "next_auth"."accounts" validate constraint "accounts_userId_fkey";

alter table "next_auth"."accounts" add constraint "provider_unique" UNIQUE using index "provider_unique";

alter table "next_auth"."sessions" add constraint "sessions_userId_fkey" FOREIGN KEY ("userId") REFERENCES next_auth.users(id) ON DELETE CASCADE not valid;

alter table "next_auth"."sessions" validate constraint "sessions_userId_fkey";

alter table "next_auth"."sessions" add constraint "sessiontoken_unique" UNIQUE using index "sessiontoken_unique";

alter table "next_auth"."users" add constraint "email_unique" UNIQUE using index "email_unique";

alter table "next_auth"."verification_tokens" add constraint "token_identifier_unique" UNIQUE using index "token_identifier_unique";

set check_function_bodies = off;

CREATE OR REPLACE FUNCTION next_auth.uid()
RETURNS uuid
LANGUAGE sql
STABLE
AS $function$
select
coalesce(
nullif(current_setting('request.jwt.claim.sub', true), ''),
(nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub')
)::uuid
$function$
;
Loading

0 comments on commit 432fbf2

Please sign in to comment.