Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion apps/web/src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Navbar from "@/components/shared/navbar";
import DashNavItem from "@/components/dash/shared/DashNavItem";
import ClientToast from "@/components/shared/client-toast";
import c from "config";
import { ADMIN_ROLES } from "@/lib/constants";

export default async function AdminLayout({
children,
Expand All @@ -24,7 +25,11 @@ export default async function AdminLayout({
where: eq(users.clerkID, userId),
});

if (!user || (user.role !== "admin" && user.role !== "super_admin")) {
if (!user) {
return redirect("/onboarding");
}

if (!ADMIN_ROLES.includes(user.role)) {
console.log("Denying admin access to user", user);
return (
<FullScreenMessage
Expand Down
2 changes: 0 additions & 2 deletions apps/web/src/app/onboarding/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ export default async function OnboardingLayout({
}: {
children: React.ReactNode;
}) {
// TODO: protect stuffs from re-registration

const { userId } = await auth();

if (!userId) return redirect("/sign-up");
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import Navbar from "@/components/shared/navbar";
import Link from "next/link";
export default function Home() {
return (
// bg-[var(--my-var,var(--my-background,pink))]
<div className=" flex h-[100dvh] w-screen flex-col">
<header>
<Navbar showBorder />
</header>
<main className="flex w-full flex-1 items-center justify-center">
<main className="flex w-full flex-1 flex-col items-center justify-center space-y-5">
<h1 className="text-4xl font-black">ClubKit</h1>
<Link href="/events" className="h-min p-2 underline">
<p>Find Events →</p>
</Link>
</main>
</div>
);
Expand Down
77 changes: 42 additions & 35 deletions apps/web/src/components/shared/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import {
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";

import { ADMIN_ROLES } from "@/lib/constants";
import c from "config";
import { Menu } from "lucide-react";
import { SignOutButton } from "@clerk/nextjs";

type NavbarProps = {
siteRegion?: string;
Expand All @@ -33,8 +34,8 @@ export default async function Navbar({ siteRegion, showBorder }: NavbarProps) {
with: { data: true },
})
: null;

const registrationComplete = user != null;
const hasSignedIn = userId != null;
const hasCompletedRegistration = user != null;
return (
<div
className={
Expand Down Expand Up @@ -64,35 +65,40 @@ export default async function Navbar({ siteRegion, showBorder }: NavbarProps) {

{/* Large screen navbar */}
<div className="my-2 hidden items-center justify-end gap-x-2 md:flex">
{user ? (
{hasSignedIn ? (
<>
<Link
href={registrationComplete ? "/dash" : "/sign-up"}
href={
hasCompletedRegistration
? "/dash"
: "/onboarding"
}
>
<Button
variant={
registrationComplete ? "outline" : "default"
hasCompletedRegistration
? "outline"
: "default"
}
>
{registrationComplete
{hasCompletedRegistration
? "Dashboard"
: "Complete Registration"}
</Button>
</Link>
<Link href={"/events"}>
<Button variant={"outline"}>Events</Button>
</Link>
{(user.role === "admin" ||
user.role === "super_admin") && (
<Link href={"/admin"}>
<Button
variant={"outline"}
className="text-blue-400"
>
Admin
</Button>
</Link>
)}

{hasCompletedRegistration &&
ADMIN_ROLES.includes(user.role) && (
<Link href={"/admin"}>
<Button
variant={"outline"}
className="text-blue-400"
>
Admin
</Button>
</Link>
)}

<ProfileButton
clerkUser={clerkUser}
clerkAuth={clerkAuth}
Expand Down Expand Up @@ -123,9 +129,9 @@ export default async function Navbar({ siteRegion, showBorder }: NavbarProps) {
<Menu />
</SheetTrigger>
<SheetContent className="flex max-w-[40%] flex-col-reverse items-center justify-center gap-y-1">
{user ? (
{hasSignedIn ? (
<>
{registrationComplete && (
{hasCompletedRegistration && (
<Link href="/settings">
<Button variant="ghost">
Settings
Expand All @@ -134,32 +140,33 @@ export default async function Navbar({ siteRegion, showBorder }: NavbarProps) {
)}
<Link
href={
registrationComplete
hasCompletedRegistration
? "/dash"
: "/onboarding"
}
>
<Button
variant={
registrationComplete
hasCompletedRegistration
? "ghost"
: "default"
}
className="h-12 whitespace-normal p-4"
>
{registrationComplete
{hasCompletedRegistration
? "Dashboard"
: "Complete Registration"}
</Button>
</Link>
<Link href={"/events"}>
<Button variant={"ghost"}>Events</Button>
</Link>
{(user.role === "admin" ||
user.role === "super_admin") && (
<Link href={"/admin"}>
<Button variant={"ghost"}>Admin</Button>
</Link>
)}

{hasCompletedRegistration &&
ADMIN_ROLES.includes(user.role) && (
<Link href={"/admin"}>
<Button variant={"ghost"}>
Admin
</Button>
</Link>
)}
<div className="px-4">
<ProfileButton
clerkUser={clerkUser}
Expand Down
2 changes: 0 additions & 2 deletions apps/web/src/components/shared/profile-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
Expand Down Expand Up @@ -104,7 +103,6 @@ export default async function ProfileButton({
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownSwitcher />

<Link href={`https://tally.so/r/wbKXN1`} target="_blank">
<DropdownMenuItem className="cursor-pointer">
Report a Bug
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/lib/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export const TWENTY_FOUR_HOURS = 24;
export const UNIQUE_KEY_CONSTRAINT_VIOLATION_CODE = "23505";
export const LOWER_ALPHANUM_CUSTOM_ALPHABET =
"1234567890abcdefghijklmnopqrstuvwxyz";

export const ADMIN_ROLES: Array<string> = ["admin", "super_admin"] as const;
5 changes: 4 additions & 1 deletion apps/web/src/lib/queries/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import c from "config";
import { count, db, eq, sum } from "db";
import { checkins, data, events, users } from "db/schema";
import { getCurrentSemester } from "./semesters";
import { ADMIN_ROLES } from "../constants";

type UserRoles = (typeof users.$inferSelect.role)[];

export const getAdminUser = async (clerkId: string) => {
return db.query.users.findFirst({
where: (users, { eq, and, inArray }) =>
and(
eq(users.clerkID, clerkId),
inArray(users.role, ["admin", "super_admin"]),
inArray(users.role, ADMIN_ROLES as UserRoles),
),
});
};
Expand Down
11 changes: 6 additions & 5 deletions apps/web/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
import { getAdminUser } from "./lib/queries/users";
import { NextResponse } from "next/server";

const isProtectedRoute = createRouteMatcher([
"/dash(.*)",
"/admin(.*)",
"/settings(.*)",
]);
const isAdminAPIRoute = createRouteMatcher(["/api/admin(.*)"]);

// come back and check if this is valid
export default clerkMiddleware(async (auth, req) => {
const { userId } = await auth();

if (isProtectedRoute(req)) {
await auth.protect();
const { userId, redirectToSignIn } = await auth();
if (isProtectedRoute(req) && !userId) {
redirectToSignIn({
returnBackUrl: req.nextUrl.toString(),
});
}

// protect admin api routes
Expand Down