Upgraded to Next Auth v5
This commit is contained in:
parent
a3352af981
commit
4505654a05
@ -9,9 +9,6 @@ export async function GET(req: Request) {
|
||||
const scope = searchParams.get('scope') as string
|
||||
const state = searchParams.get('state') as string
|
||||
|
||||
console.log("CODE:", code)
|
||||
console.log("SCOPE:", scope)
|
||||
console.log("STATE:", state)
|
||||
if (!code || !scope || !state) {
|
||||
return new NextResponse("Bad Request", { status: 400 });
|
||||
}
|
||||
@ -38,21 +35,17 @@ export async function GET(req: Request) {
|
||||
|
||||
// Fetch values from token.
|
||||
const { access_token, expires_in, refresh_token, token_type } = token
|
||||
// console.log("AT", access_token)
|
||||
// console.log("RT", refresh_token)
|
||||
// console.log("TT", token_type)
|
||||
|
||||
if (!access_token || !refresh_token || token_type !== "bearer") {
|
||||
return new NextResponse("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
let info = await axios.get("https://api.twitch.tv/helix/users?login=" + user.username, {
|
||||
let info = await axios.get("https://api.twitch.tv/helix/users?login=" + user.name, {
|
||||
headers: {
|
||||
"Authorization": "Bearer " + access_token,
|
||||
"Client-Id": process.env.TWITCH_BOT_CLIENT_ID
|
||||
}
|
||||
})
|
||||
console.log(info.data.data)
|
||||
const broadcasterId = info.data.data[0]['id']
|
||||
|
||||
await db.twitchConnection.create({
|
||||
|
@ -1,7 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import { db } from "@/lib/db"
|
||||
import { NextResponse } from "next/server";
|
||||
import { GET as authorize } from '../authorize/route'
|
||||
|
||||
export async function GET(req: Request) {
|
||||
try {
|
||||
@ -11,7 +10,6 @@ export async function GET(req: Request) {
|
||||
id: req.headers.get('x-api-key') as string
|
||||
}
|
||||
})
|
||||
|
||||
if (!key) {
|
||||
return new NextResponse("Forbidden", { status: 403 });
|
||||
}
|
||||
@ -46,9 +44,6 @@ export async function GET(req: Request) {
|
||||
|
||||
// Fetch values from token.
|
||||
const { access_token, expires_in, refresh_token, token_type } = token
|
||||
// console.log("AT", access_token)
|
||||
// console.log("RT", refresh_token)
|
||||
// console.log("TT", token_type)
|
||||
|
||||
if (!access_token || !refresh_token || token_type !== "bearer") {
|
||||
return new NextResponse("Unauthorized", { status: 401 });
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { db } from "@/lib/db"
|
||||
import { NextResponse } from "next/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { generateToken } from "../token/route";
|
||||
import { auth } from "@/auth";
|
||||
import fetchUserUsingAPI from "@/lib/validate-api";
|
||||
|
||||
|
||||
@ -16,7 +15,7 @@ export async function GET(req: Request) {
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const session = await getServerSession()
|
||||
const session = await auth()
|
||||
const user = session?.user?.name
|
||||
if (!user) {
|
||||
return new NextResponse("Internal Error", { status: 401 })
|
||||
@ -24,26 +23,26 @@ export async function POST(req: Request) {
|
||||
|
||||
const exist = await db.user.findFirst({
|
||||
where: {
|
||||
username: user.toLowerCase() as string
|
||||
name: user
|
||||
}
|
||||
});
|
||||
|
||||
if (exist) {
|
||||
return NextResponse.json({
|
||||
id: exist.id,
|
||||
username: exist.username
|
||||
username: exist.name
|
||||
});
|
||||
}
|
||||
|
||||
const newUser = await db.user.create({
|
||||
data: {
|
||||
username: user.toLowerCase() as string,
|
||||
name: user,
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
id: newUser.id,
|
||||
username: newUser.username
|
||||
username: newUser.name
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("[ACCOUNT]", error);
|
||||
|
@ -1,40 +0,0 @@
|
||||
import type { NextAuthOptions } from "next-auth";
|
||||
import TwitchProvider from "next-auth/providers/twitch";
|
||||
|
||||
export interface TwitchProfile extends Record<string, any> {
|
||||
sub: string
|
||||
preferred_username: string
|
||||
email: string
|
||||
picture: string
|
||||
}
|
||||
|
||||
export const options: NextAuthOptions = {
|
||||
providers: [
|
||||
TwitchProvider({
|
||||
clientId: process.env.TWITCH_CLIENT_ID as string,
|
||||
clientSecret: process.env.TWITCH_CLIENT_SECRET as string,
|
||||
authorization: {
|
||||
params: {
|
||||
scope: "openid user:read:email",
|
||||
claims: {
|
||||
id_token: {
|
||||
email: null,
|
||||
picture: null,
|
||||
preferred_username: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
idToken: true,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.preferred_username,
|
||||
email: profile.email,
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
})
|
||||
],
|
||||
secret: process.env.NEXTAUTH_SECRET
|
||||
}
|
@ -1,6 +1 @@
|
||||
import NextAuth from 'next-auth'
|
||||
import { options } from './options'
|
||||
|
||||
const handler = NextAuth(options)
|
||||
|
||||
export { handler as GET, handler as POST }
|
||||
export { GET, POST } from "@/auth"
|
@ -1,4 +1,3 @@
|
||||
import axios from "axios"
|
||||
import { db } from "@/lib/db"
|
||||
import { NextResponse } from "next/server";
|
||||
import fetchUserUsingAPI from "@/lib/validate-api";
|
||||
@ -7,7 +6,6 @@ export async function GET(req: Request) {
|
||||
try {
|
||||
const user = await fetchUserUsingAPI(req)
|
||||
if (!user) {
|
||||
console.log("TWITCH CONNECT", user)
|
||||
return new NextResponse("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
@ -32,53 +30,3 @@ export async function GET(req: Request) {
|
||||
return new NextResponse("Internal Error", { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const { id, secret } = await req.json();
|
||||
const user = await fetchUserUsingAPI(req)
|
||||
|
||||
if (!user) {
|
||||
return new NextResponse("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
response = await axios.post("https://id.twitch.tv/oauth2/token", {
|
||||
client_id: id,
|
||||
client_secret: secret,
|
||||
grant_type: "client_credentials"
|
||||
});
|
||||
console.log(response.data)
|
||||
} catch (error) {
|
||||
console.log("[CONNECTIONS/TWITCH/TOKEN]", error);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(user.username)
|
||||
let info = await axios.get("https://api.twitch.tv/helix/users?login=" + user.username, {
|
||||
headers: {
|
||||
"Authorization": "Bearer " + response.data['access_token'],
|
||||
"Client-Id": id
|
||||
}
|
||||
})
|
||||
console.log(info.data.data)
|
||||
const broadcasterId = info.data.data[0]['id']
|
||||
const username = info.data.data[0]['login']
|
||||
|
||||
const connection = await db.twitchConnection.create({
|
||||
data: {
|
||||
id: id,
|
||||
secret,
|
||||
userId: user.id as string,
|
||||
broadcasterId,
|
||||
username
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json(connection);
|
||||
} catch (error) {
|
||||
console.log("[CONNECTION/TWITCH]", error);
|
||||
return new NextResponse("Internal Error", { status: 500 });
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@ export async function GET(req: Request) {
|
||||
userId: user.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!api) {
|
||||
return new NextResponse("Forbidden", { status: 403 });
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
import { db } from "@/lib/db"
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET(req: Request, { params } : { params: { id: string } }) {
|
||||
try {
|
||||
let id = req.headers.get('x-api-key')
|
||||
if (id == null) {
|
||||
return NextResponse.json(null);
|
||||
}
|
||||
|
||||
const tokens = await db.apiKey.findFirst({
|
||||
where: {
|
||||
id: id as string
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json(tokens);
|
||||
} catch (error) {
|
||||
console.log("[VALIDATE/GET]", error);
|
||||
return new NextResponse("Internal Error", { status: 500});
|
||||
}
|
||||
}
|
9
app/auth/layout.tsx
Normal file
9
app/auth/layout.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
const AuthLayout = ({ children } : { children: React.ReactNode }) => {
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center bg-black">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthLayout;
|
9
app/auth/login/page.tsx
Normal file
9
app/auth/login/page.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import { LoginForm } from "@/components/auth/login-form";
|
||||
|
||||
const LoginPage = () => {
|
||||
return (
|
||||
<LoginForm />
|
||||
);
|
||||
}
|
||||
|
||||
export default LoginPage;
|
@ -7,14 +7,13 @@ const SettingsLayout = async (
|
||||
{ children } : { children:React.ReactNode } ) => {
|
||||
const headersList = headers();
|
||||
const header_url = headersList.get('x-url') || "";
|
||||
console.log("HEADER URL: " + header_url)
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
<div className={cn("hidden md:flex h-full w-[250px] z-30 flex-col fixed inset-y-0", header_url.endsWith("/settings") && "md:flex h-full w-full z-30 flex-col fixed inset-y-0")}>
|
||||
<SettingsNavigation />
|
||||
</div>
|
||||
|
||||
{header_url}
|
||||
<main className={cn("md:pl-[250px] h-full", header_url.endsWith("/settings") && "hidden")}>
|
||||
{children}
|
||||
</main>
|
||||
|
@ -1,8 +1,9 @@
|
||||
"use client";
|
||||
import { auth } from "@/auth";
|
||||
|
||||
const SettingsPage = () => {
|
||||
const SettingsPage = async () => {
|
||||
return (
|
||||
<div>
|
||||
{JSON.stringify(await auth())}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
17
auth.config.ts
Normal file
17
auth.config.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import Twitch from "next-auth/providers/twitch"
|
||||
|
||||
import type { NextAuthConfig } from "next-auth"
|
||||
|
||||
export default {
|
||||
providers: [
|
||||
Twitch({
|
||||
clientId: process.env.TWITCH_CLIENT_ID,
|
||||
clientSecret: process.env.TWITCH_CLIENT_SECRET,
|
||||
authorization: {
|
||||
params: {
|
||||
scope: "openid user:read:email",
|
||||
},
|
||||
}
|
||||
})
|
||||
],
|
||||
} satisfies NextAuthConfig
|
35
auth.ts
Normal file
35
auth.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import NextAuth from "next-auth"
|
||||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||
|
||||
import { db } from "@/lib/db"
|
||||
import authConfig from "@/auth.config"
|
||||
|
||||
export const {
|
||||
handlers: { GET, POST },
|
||||
auth,
|
||||
signIn,
|
||||
signOut,
|
||||
} = NextAuth({
|
||||
events: {
|
||||
async linkAccount({ user }) {
|
||||
await db.user.update({
|
||||
where: {id: user.id },
|
||||
data: { emailVerified: new Date() }
|
||||
})
|
||||
}
|
||||
},
|
||||
callbacks: {
|
||||
async session({ session, user, token }) {
|
||||
if (token.sub && session.user) {
|
||||
session.user.id = token.sub
|
||||
}
|
||||
return session
|
||||
},
|
||||
async jwt({ token, user, account, profile, isNewUser }) {
|
||||
return token
|
||||
}
|
||||
},
|
||||
adapter: PrismaAdapter(db),
|
||||
session: { strategy: "jwt" },
|
||||
...authConfig,
|
||||
})
|
40
components/auth/card-wrapper.tsx
Normal file
40
components/auth/card-wrapper.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
"use client"
|
||||
|
||||
import { LoginForm } from "@/components/auth/login-form";
|
||||
import React from "react";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle
|
||||
} from "@/components/ui/card";
|
||||
import { Header } from "@/components/auth/header";
|
||||
import { Social } from "@/components/auth/social";
|
||||
|
||||
|
||||
interface CardWrapperProps {
|
||||
children: React.ReactNode
|
||||
headerLabel: string
|
||||
}
|
||||
|
||||
export const CardWrapper = ({
|
||||
children,
|
||||
headerLabel
|
||||
}: CardWrapperProps) => {
|
||||
return (
|
||||
<Card className="w-[400px] shadow-md bg-white text-black">
|
||||
<CardHeader>
|
||||
<Header label={headerLabel} />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{children}
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Social />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default CardWrapper
|
25
components/auth/header.tsx
Normal file
25
components/auth/header.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Poppins } from "next/font/google";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
const font = Poppins({
|
||||
subsets: ["latin"],
|
||||
weight: ["600"]
|
||||
})
|
||||
|
||||
interface HeaderProps {
|
||||
label: string
|
||||
}
|
||||
|
||||
export const Header = ({ label }: HeaderProps) => {
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-y-4 items-center justify-center">
|
||||
<h1 className={cn(
|
||||
"text-3xl font-semibold", font.className
|
||||
)}>Login</h1>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{label}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
11
components/auth/login-form.tsx
Normal file
11
components/auth/login-form.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { CardWrapper } from "./card-wrapper"
|
||||
|
||||
export const LoginForm = () => {
|
||||
return (
|
||||
<CardWrapper
|
||||
headerLabel="Welcome back"
|
||||
>
|
||||
Login Form
|
||||
</CardWrapper>
|
||||
)
|
||||
}
|
27
components/auth/social.tsx
Normal file
27
components/auth/social.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
"use client"
|
||||
|
||||
import { signIn } from "next-auth/react"
|
||||
import { FaTwitch } from "react-icons/fa"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { DEFAULT_REDIRECT } from "@/routes"
|
||||
|
||||
export const Social = () => {
|
||||
const onClick = (provider: "twitch") => {
|
||||
signIn(provider, {
|
||||
callbackUrl: DEFAULT_REDIRECT,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center w-full gap-x-2">
|
||||
<Button
|
||||
size="lg"
|
||||
variant="outline"
|
||||
className="w-full bg-white hover:bg-purple-500"
|
||||
onClick={() => onClick("twitch")}
|
||||
>
|
||||
<FaTwitch className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -2,40 +2,37 @@
|
||||
|
||||
import axios from "axios";
|
||||
import * as React from 'react';
|
||||
import { User } from "@prisma/client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const UserProfile = () => {
|
||||
const { data: session, status } = useSession();
|
||||
const [previousUsername, setPreviousUsername] = useState<string>()
|
||||
const [user, setUser] = useState<User>()
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
const pathname = usePathname()
|
||||
const [user, setUser] = useState<{ id: string, username: string }>()
|
||||
|
||||
let previousUsername = ""
|
||||
|
||||
useEffect(() => {
|
||||
if (status !== "authenticated" || previousUsername == session.user?.name) {
|
||||
return
|
||||
}
|
||||
|
||||
setPreviousUsername(session.user?.name as string)
|
||||
previousUsername = session.user?.name || ""
|
||||
if (session.user) {
|
||||
const fetchData = async () => {
|
||||
var userData: User = (await axios.get("/api/account")).data
|
||||
var userData = (await axios.get("/api/account")).data
|
||||
setUser(userData)
|
||||
setLoading(false)
|
||||
console.log(userData)
|
||||
}
|
||||
|
||||
fetchData().catch(console.error)
|
||||
|
||||
// TODO: check cookies if impersonation is in use.
|
||||
// TODO: check session if impersonation is in use.
|
||||
}
|
||||
}, [session])
|
||||
|
||||
return (
|
||||
<div className={cn("px-10 py-6 rounded-md bg-blue-300 overflow-hidden wrap", loading && "hidden")}>
|
||||
<div className={cn("px-10 py-6 rounded-md bg-blue-300 overflow-hidden wrap", user == null && "hidden")}>
|
||||
<p className="text-xs text-gray-400">Logged in as:</p>
|
||||
<p>{user?.username}</p>
|
||||
</div>
|
||||
|
10
data/user.ts
Normal file
10
data/user.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { db } from "@/lib/db";
|
||||
|
||||
export const getUserById = async (id: string) => {
|
||||
try {
|
||||
const user = await db.user.findUnique({ where: { id }})
|
||||
return user;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,25 +1,23 @@
|
||||
import { getServerSession } from "next-auth";
|
||||
import { auth } from "@/auth";
|
||||
import { db } from "./db";
|
||||
|
||||
export default async function fetchUserUsingAPI(req: Request) {
|
||||
const session = await getServerSession()
|
||||
console.log("server session:", session)
|
||||
const session = await auth()
|
||||
|
||||
if (session) {
|
||||
const user = await db.user.findFirst({
|
||||
where: {
|
||||
username: session.user?.name?.toLowerCase() as string
|
||||
name: session.user?.name
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id: user?.id,
|
||||
username: user?.username
|
||||
username: user?.name
|
||||
}
|
||||
}
|
||||
|
||||
const token = req.headers?.get('x-api-key')
|
||||
console.log("x-api-key:", token)
|
||||
if (token === null || token === undefined)
|
||||
return null
|
||||
|
||||
@ -35,10 +33,8 @@ export default async function fetchUserUsingAPI(req: Request) {
|
||||
}
|
||||
})
|
||||
|
||||
console.log("user:", user)
|
||||
|
||||
return {
|
||||
id: user?.id,
|
||||
username: user?.username
|
||||
username: user?.name
|
||||
}
|
||||
}
|
@ -1,64 +1,43 @@
|
||||
// import { authMiddleware } from "@clerk/nextjs";
|
||||
// import { NextResponse } from "next/server";
|
||||
import authConfig from "@/auth.config"
|
||||
import NextAuth from "next-auth"
|
||||
|
||||
// // This example protects all routes including api/trpc routes
|
||||
// // Please edit this to allow other routes to be public as needed.
|
||||
// // See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your middleware
|
||||
// export default authMiddleware({
|
||||
// publicRoutes: ["/api/:path*"],
|
||||
// ignoredRoutes: ["/api/validate/:path*"],
|
||||
import {
|
||||
DEFAULT_REDIRECT,
|
||||
PUBLIC_ROUTES,
|
||||
AUTH_ROUTES,
|
||||
API_PREFIX
|
||||
} from "@/routes"
|
||||
|
||||
// beforeAuth: async (req) => {
|
||||
// // if (req.url.startsWith("https://localhost:3000/api") /*&& !req.url.startsWith("https://localhost:3000/api/validate/")*/) {
|
||||
// // const apiKey = req.headers.get("x-api-key") as string
|
||||
// // let api = null
|
||||
// // if (apiKey != null) {
|
||||
// // console.log("API KEY:", apiKey)
|
||||
// // api = await fetch("http://localhost:3000/api/validate")
|
||||
// // }
|
||||
// // if (api == null) {
|
||||
// // console.log("Invalid API key attempted")
|
||||
// // return NextResponse.rewrite(
|
||||
// // `${req.nextUrl.protocol}//${req.nextUrl.host}`,
|
||||
// // {
|
||||
// // status: 401,
|
||||
// // headers: {
|
||||
// // "WWW-Authenticate": 'Basic realm="Secure Area"',
|
||||
// // },
|
||||
// // }
|
||||
// // );
|
||||
// // }
|
||||
// // }
|
||||
const { auth } = NextAuth(authConfig)
|
||||
|
||||
// return NextResponse.next();
|
||||
// }
|
||||
// });
|
||||
export default auth((req) => {
|
||||
const isLoggedIn = !!req.auth
|
||||
|
||||
// export const config = {
|
||||
// matcher: ["/((?!.*\\..*|_next).*)", "/", "/(trpc)(.*)"],
|
||||
// };
|
||||
const { nextUrl } = req
|
||||
|
||||
import { NextResponse } from "next/server";
|
||||
import { redirect } from 'next/navigation';
|
||||
import { withAuth } from 'next-auth/middleware';
|
||||
import { getServerSession } from "next-auth";
|
||||
const isApiRoute = nextUrl.pathname.startsWith(API_PREFIX)
|
||||
const isPublicRoute = PUBLIC_ROUTES.includes(nextUrl.pathname)
|
||||
const isAuthRoute = AUTH_ROUTES.includes(nextUrl.pathname)
|
||||
|
||||
export default withAuth(
|
||||
async function middleware(req) {
|
||||
const requestHeaders = new Headers(req.headers);
|
||||
requestHeaders.set('x-url', req.url);
|
||||
|
||||
return NextResponse.next({
|
||||
request: {
|
||||
// Apply new request headers
|
||||
headers: requestHeaders,
|
||||
if (isApiRoute) {
|
||||
return null
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
callbacks: {
|
||||
authorized: async ({ req, token }) =>
|
||||
req.nextUrl.pathname?.slice(0, 4) === '/api' ||
|
||||
!!token
|
||||
|
||||
if (isAuthRoute) {
|
||||
if (isLoggedIn) {
|
||||
return Response.redirect(new URL(DEFAULT_REDIRECT, nextUrl))
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isLoggedIn && !isPublicRoute) {
|
||||
return Response.redirect(new URL("/auth/login", nextUrl))
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
// Optionally, don't invoke Middleware on some paths
|
||||
export const config = {
|
||||
matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
|
||||
}
|
@ -9,10 +9,14 @@ datasource db {
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
username String @unique
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
|
||||
apiKeys ApiKey[]
|
||||
accounts Account[]
|
||||
twitchConnections TwitchConnection[]
|
||||
createdProfiles TtsProfile[]
|
||||
profileStatus TtsProfileStatus[]
|
||||
@ -21,6 +25,25 @@ model User {
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String? @db.Text
|
||||
access_token String? @db.Text
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String? @db.Text
|
||||
session_state String?
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model ApiKey {
|
||||
id String @id @default(uuid())
|
||||
label String
|
||||
@ -88,7 +111,7 @@ model TtsBadgeFilter {
|
||||
|
||||
model TtsUsernameFilter {
|
||||
username String
|
||||
white Boolean
|
||||
tag String
|
||||
|
||||
profileId String
|
||||
profile TtsProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
|
||||
|
Loading…
Reference in New Issue
Block a user