Added basic user roles
This commit is contained in:
parent
ca9d84a25a
commit
d8a72dcac0
39
auth.ts
39
auth.ts
@ -1,8 +1,32 @@
|
||||
import NextAuth from "next-auth"
|
||||
import NextAuth, { DefaultSession } from "next-auth"
|
||||
import { JWT } from "@auth/core/jwt"
|
||||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||
|
||||
import { db } from "@/lib/db"
|
||||
import authConfig from "@/auth.config"
|
||||
import { getUserById } from "./data/user"
|
||||
import { UserRole } from "@prisma/client"
|
||||
|
||||
|
||||
declare module "@auth/core/types" {
|
||||
/**
|
||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
||||
*/
|
||||
interface Session {
|
||||
user: {
|
||||
role: UserRole
|
||||
// By default, TypeScript merges new interface properties and overwrite existing ones. In this case, the default session user properties will be overwritten, with the new one defined above. To keep the default session user properties, you need to add them back into the newly declared interface
|
||||
} & DefaultSession["user"] // To keep the default types
|
||||
}
|
||||
}
|
||||
|
||||
declare module "@auth/core/jwt" {
|
||||
/** Returned by the `jwt` callback and `auth`, when using JWT sessions */
|
||||
interface JWT {
|
||||
role: UserRole
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const {
|
||||
handlers: { GET, POST },
|
||||
@ -23,9 +47,22 @@ export const {
|
||||
if (token.sub && session.user) {
|
||||
session.user.id = token.sub
|
||||
}
|
||||
|
||||
if (token.role && session.user) {
|
||||
session.user.role = token.role
|
||||
}
|
||||
|
||||
return session
|
||||
},
|
||||
async jwt({ token, user, account, profile, isNewUser }) {
|
||||
if (!token.sub) return token
|
||||
|
||||
const existingUser = await getUserById(token.sub)
|
||||
|
||||
if (!existingUser) return token
|
||||
|
||||
token.role = existingUser.role
|
||||
|
||||
return token
|
||||
}
|
||||
},
|
||||
|
30
components/auth/role-gate.tsx
Normal file
30
components/auth/role-gate.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
"use client"
|
||||
|
||||
import { UserRole } from "@prisma/client";
|
||||
import { useSession } from "next-auth/react";
|
||||
import React from "react";
|
||||
|
||||
|
||||
interface RoleGateProps {
|
||||
children: React.ReactNode
|
||||
roles: UserRole[]
|
||||
}
|
||||
|
||||
export const RoleGate = ({
|
||||
children,
|
||||
roles,
|
||||
}: RoleGateProps) => {
|
||||
const session = useSession()
|
||||
const role = session?.data?.user.role as UserRole
|
||||
|
||||
if (roles.includes(role)) {
|
||||
return (
|
||||
<div>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div />
|
||||
}
|
||||
|
||||
export default RoleGate
|
22
components/navigation/adminprofile.tsx
Normal file
22
components/navigation/adminprofile.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
"use client"
|
||||
|
||||
import axios from "axios";
|
||||
import * as React from 'react';
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const AdminProfile = () => {
|
||||
const session = useSession();
|
||||
const [user, setUser] = useState<{ id: string, username: string }>()
|
||||
|
||||
|
||||
return (
|
||||
<div className={"px-10 py-6 rounded-md bg-red-300 overflow-hidden wrap m-[10px]"}>
|
||||
<p className="text-xs text-gray-400">Role:</p>
|
||||
<p>{session?.data?.user?.role}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AdminProfile;
|
@ -1,6 +1,8 @@
|
||||
import Link from "next/link";
|
||||
import { Button } from "../ui/button";
|
||||
import UserProfile from "./userprofile";
|
||||
import AdminProfile from "./adminprofile";
|
||||
import RoleGate from "../auth/role-gate";
|
||||
|
||||
const SettingsNavigation = async () => {
|
||||
return (
|
||||
@ -8,7 +10,12 @@ const SettingsNavigation = async () => {
|
||||
<div className="text-4xl flex pl-[15px] pb-[33px]">Hermes</div>
|
||||
|
||||
<div className="w-full pl-[30px] pr-[30px] pb-[50px]">
|
||||
<UserProfile />
|
||||
<div className="gap-5">
|
||||
<UserProfile />
|
||||
<RoleGate roles={["ADMIN"]}>
|
||||
<AdminProfile />
|
||||
</RoleGate>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-full z-20 inset-y-1/3 w-full">
|
||||
|
@ -20,14 +20,13 @@ const UserProfile = () => {
|
||||
previousUsername = session.user?.name || ""
|
||||
if (session.user) {
|
||||
const fetchData = async () => {
|
||||
if (user) return
|
||||
|
||||
var userData = (await axios.get("/api/account")).data
|
||||
setUser(userData)
|
||||
console.log(userData)
|
||||
}
|
||||
|
||||
fetchData().catch(console.error)
|
||||
|
||||
// TODO: check session if impersonation is in use.
|
||||
}
|
||||
}, [session])
|
||||
|
||||
|
@ -1,16 +1,9 @@
|
||||
import { db } from "@/lib/db"
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
export const currentUser = async() => {
|
||||
const { data: session, status } = useSession()
|
||||
|
||||
if (status !== "authenticated") {
|
||||
return null;
|
||||
}
|
||||
|
||||
export const currentUser = async(id: string) => {
|
||||
const user = await db.user.findUnique({
|
||||
where: {
|
||||
id: session?.user?.name as string
|
||||
id
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -8,11 +8,17 @@ datasource db {
|
||||
relationMode = "prisma"
|
||||
}
|
||||
|
||||
enum UserRole {
|
||||
USER
|
||||
ADMIN
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
role UserRole @default(USER)
|
||||
image String?
|
||||
ttsDefaultVoice Int @default(1)
|
||||
ttsEnabledVoice Int @default(1048575)
|
||||
|
Loading…
Reference in New Issue
Block a user