refactor: streamline authentication flow by removing unused code, replacing custom auth utilities with NextAuth, and updating session handling in components
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 5m56s

This commit is contained in:
2025-03-28 22:24:45 +05:00
parent 99c5fef748
commit ef9f032366
10 changed files with 291 additions and 323 deletions

View File

@ -1,56 +1,54 @@
'use client'
import { Button } from "@/components/ui/button"
"use client";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import { authClient } from "@/lib/auth-client";
import { Loader2, User as UserIcon } from "lucide-react"
import { useRouter } from "next/navigation"
import { useState } from "react"
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Loader2, User as UserIcon } from "lucide-react";
import { signOut, useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useState } from "react";
export function AccountPopover() {
const session = authClient.useSession();
const [loading, setLoading] = useState(false)
const router = useRouter()
const session = useSession();
const [loading, setLoading] = useState(false);
const router = useRouter();
if (session.isPending) {
<Button variant={"outline"} disabled>
<Loader2 className="animate-spin" />
</Button>
}
return (
<Popover>
<PopoverTrigger asChild>
<Button className="w-fit px-2" variant="outline">
<UserIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-fit">
<div className="grid gap-4">
<div className="space-y-2">
<h4 className="font-medium leading-none">{session.data?.user?.name}</h4>
<p className="text-sm text-muted-foreground">
{session.data?.user?.phoneNumber}
</p>
</div>
<Button disabled={loading} onClick={async () => {
setLoading(true)
await authClient.signOut({
fetchOptions: {
onSuccess: () => {
router.push("/login"); // redirect to login page
},
},
})
setLoading(false)
}}>
{loading ? <Loader2 className="animate-spin" /> : "Logout"}
</Button>
</div>
</PopoverContent>
</Popover>
)
if (session.status === "loading") {
<Button variant={"outline"} disabled>
<Loader2 className="animate-spin" />
</Button>;
}
return (
<Popover>
<PopoverTrigger asChild>
<Button className="w-fit px-2" variant="outline">
<UserIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-fit">
<div className="grid gap-4">
<div className="space-y-2">
<h4 className="font-medium leading-none">
{session.data?.user?.name}
</h4>
<p className="text-sm text-muted-foreground">
{session.data?.user?.phoneNumber}
</p>
</div>
<Button
disabled={loading}
onClick={async () => {
setLoading(true);
await signOut();
setLoading(false);
}}
>
{loading ? <Loader2 className="animate-spin" /> : "Logout"}
</Button>
</div>
</PopoverContent>
</Popover>
);
}

View File

@ -4,33 +4,26 @@ import { Wallet } from "@/components/wallet";
import { ModeToggle } from "@/components/theme-toggle";
import { AppSidebar } from "@/components/ui/app-sidebar";
import { authOptions } from "@/app/auth";
import { Separator } from "@/components/ui/separator";
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar";
import { auth } from "@/app/auth";
import prisma from "@/lib/db";
import { getServerSession } from "next-auth";
import { headers } from "next/headers";
import { AccountPopover } from "./account-popver";
export async function ApplicationLayout({
children,
}: { children: React.ReactNode }) {
const session = await auth.api.getSession({
headers: await headers(),
});
const billFormula = await prisma.billFormula.findFirst();
const user = await prisma.user.findFirst({
where: {
id: session?.user?.id,
},
});
const session = await getServerSession(authOptions);
return (
<SidebarProvider>
<AppSidebar role={session?.user?.role || "USER"} />
<DeviceCartDrawer billFormula={billFormula || null} />
<AppSidebar role={"admin"} />
{/* <DeviceCartDrawer billFormula={billFormula || null} /> */}
<SidebarInset>
<header className="flex justify-between sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4 z-10">
<div className="flex items-center gap-2 ">
@ -44,7 +37,7 @@ export async function ApplicationLayout({
</div>
<div className="flex items-center gap-2">
<Wallet walletBalance={user?.walletBalance || 0} />
{/* <Wallet walletBalance={user?.walletBalance || 0} /> */}
<ModeToggle />
<AccountPopover />
</div>

View File

@ -3,7 +3,6 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { authClient } from "@/lib/auth-client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Loader2 } from "lucide-react";
import Link from "next/link";
@ -37,16 +36,16 @@ export default function VerifyOTPForm({
const onSubmit: SubmitHandler<z.infer<typeof OTPSchema>> = (data) => {
startTransition(async () => {
const isVerified = await authClient.phoneNumber.verify({
phoneNumber: phone_number,
code: data.pin,
});
console.log({ isVerified });
if (!isVerified.error) {
router.push("/devices");
} else {
toast.error(isVerified.error.message);
}
// const isVerified = await authClient.phoneNumber.verify({
// phoneNumber: phone_number,
// code: data.pin,
// });
// console.log({ isVerified });
// if (!isVerified.error) {
// router.push("/devices");
// } else {
// toast.error(isVerified.error.message);
// }
});
};
@ -70,11 +69,7 @@ export default function VerifyOTPForm({
<p className="text-red-500 text-sm">{errors.pin.message}</p>
)}
</div>
<Button
className="w-full"
disabled={isPending}
type="submit"
>
<Button className="w-full" disabled={isPending} type="submit">
{isPending ? <Loader2 className="animate-spin" /> : "Login"}
</Button>
</div>

View File

@ -1,3 +1,4 @@
import { authOptions } from "@/app/auth";
import {
Table,
TableBody,
@ -8,9 +9,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
import { auth } from "@/app/auth";
import prisma from "@/lib/db";
import { headers } from "next/headers";
import { getServerSession } from "next-auth";
import ClickableRow from "./clickable-row";
import DeviceCard from "./device-card";
import Pagination from "./pagination";
@ -26,85 +25,84 @@ export async function DevicesTable({
}>;
parentalControl?: boolean;
}) {
const session = await auth.api.getSession({
headers: await headers(),
});
const isAdmin = session?.user.role === "ADMIN";
const session = await getServerSession(authOptions);
const isAdmin = session?.user;
const query = (await searchParams)?.query || "";
const page = (await searchParams)?.page;
const sortBy = (await searchParams)?.sortBy || "asc";
const totalDevices = await prisma.device.count({
where: {
userId: isAdmin ? undefined : session?.session.userId,
OR: [
{
name: {
contains: query || "",
mode: "insensitive",
},
},
{
mac: {
contains: query || "",
mode: "insensitive",
},
},
],
NOT: {
payments: {
some: {
paid: false,
},
},
},
isActive: isAdmin ? undefined : parentalControl,
blocked: isAdmin
? undefined
: parentalControl !== undefined
? undefined
: false,
},
});
// const totalDevices = await prisma.device.count({
// where: {
// userId: isAdmin ? undefined : session?.session.userId,
// OR: [
// {
// name: {
// contains: query || "",
// mode: "insensitive",
// },
// },
// {
// mac: {
// contains: query || "",
// mode: "insensitive",
// },
// },
// ],
// NOT: {
// payments: {
// some: {
// paid: false,
// },
// },
// },
// isActive: isAdmin ? undefined : parentalControl,
// blocked: isAdmin
// ? undefined
// : parentalControl !== undefined
// ? undefined
// : false,
// },
// });
const totalPages = Math.ceil(totalDevices / 10);
// const totalPages = Math.ceil(totalDevices / 10);
const limit = 10;
const offset = (Number(page) - 1) * limit || 0;
const devices = await prisma.device.findMany({
where: {
userId: session?.session.userId,
OR: [
{
name: {
contains: query || "",
mode: "insensitive",
},
},
{
mac: {
contains: query || "",
mode: "insensitive",
},
},
],
NOT: {
payments: {
some: {
paid: false,
},
},
},
isActive: parentalControl,
blocked: parentalControl !== undefined ? undefined : false,
},
// const devices = await prisma.device.findMany({
// where: {
// userId: session?.session.userId,
// OR: [
// {
// name: {
// contains: query || "",
// mode: "insensitive",
// },
// },
// {
// mac: {
// contains: query || "",
// mode: "insensitive",
// },
// },
// ],
// NOT: {
// payments: {
// some: {
// paid: false,
// },
// },
// },
// isActive: parentalControl,
// blocked: parentalControl !== undefined ? undefined : false,
// },
skip: offset,
take: limit,
orderBy: {
name: `${sortBy}` as "asc" | "desc",
},
});
// skip: offset,
// take: limit,
// orderBy: {
// name: `${sortBy}` as "asc" | "desc",
// },
// });
return null;
return (
<div>
{devices.length === 0 ? (

View File

@ -2,123 +2,119 @@
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
import {
WalletDrawerOpenAtom,
walletTopUpValue,
} from "@/lib/atoms";
import { authClient } from "@/lib/auth-client";
import { WalletDrawerOpenAtom, walletTopUpValue } from "@/lib/atoms";
import type { TopupType } from "@/lib/types";
import { useAtom, } from "jotai";
import {
CircleDollarSign,
Loader2,
Wallet2,
} from "lucide-react";
import { usePathname, } from "next/navigation";
import { useAtom } from "jotai";
import { CircleDollarSign, Loader2, Wallet2 } from "lucide-react";
import { useSession } from "next-auth/react";
import { usePathname } from "next/navigation";
import { useState } from "react";
import NumberInput from "./number-input";
export function Wallet({
walletBalance,
walletBalance,
}: {
walletBalance: number;
walletBalance: number;
}) {
const session = authClient.useSession();
const pathname = usePathname();
const [amount, setAmount] = useAtom(walletTopUpValue);
const [isOpen, setIsOpen] = useAtom(WalletDrawerOpenAtom);
const [disabled, setDisabled] = useState(false);
// const router = useRouter();
const session = useSession();
const pathname = usePathname();
const [amount, setAmount] = useAtom(walletTopUpValue);
const [isOpen, setIsOpen] = useAtom(WalletDrawerOpenAtom);
const [disabled, setDisabled] = useState(false);
// const router = useRouter();
if (pathname === "/payment") {
return null;
}
if (pathname === "/payment") {
return null;
}
const data: TopupType = {
userId: session?.data?.user.id ?? "",
amount: Number.parseFloat(amount.toFixed(2)),
paid: false,
};
const data: TopupType = {
userId: session?.data?.user.id ?? "",
amount: Number.parseFloat(amount.toFixed(2)),
paid: false,
};
return (
<Drawer open={isOpen} onOpenChange={setIsOpen}>
<DrawerTrigger asChild>
<Button onClick={() => setIsOpen(!isOpen)} variant="outline">
{new Intl.NumberFormat('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(walletBalance)} MVR
<Wallet2 />
</Button>
</DrawerTrigger>
<DrawerContent>
<div className="mx-auto w-full max-w-sm">
<DrawerHeader>
<DrawerTitle>Wallet</DrawerTitle>
<DrawerDescription asChild>
<div>
Your wallet balance is{" "}
<span className="font-semibold">
{new Intl.NumberFormat('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(walletBalance)}
</span>{" "}
</div>
</DrawerDescription>
</DrawerHeader>
return (
<Drawer open={isOpen} onOpenChange={setIsOpen}>
<DrawerTrigger asChild>
<Button onClick={() => setIsOpen(!isOpen)} variant="outline">
{new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(walletBalance)}{" "}
MVR
<Wallet2 />
</Button>
</DrawerTrigger>
<DrawerContent>
<div className="mx-auto w-full max-w-sm">
<DrawerHeader>
<DrawerTitle>Wallet</DrawerTitle>
<DrawerDescription asChild>
<div>
Your wallet balance is{" "}
<span className="font-semibold">
{new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(walletBalance)}
</span>{" "}
</div>
</DrawerDescription>
</DrawerHeader>
<div className="px-4 flex flex-col gap-4">
<NumberInput
label="Set amount to top up"
value={amount}
onChange={(value) => setAmount(value)}
maxAllowed={5000}
isDisabled={amount === 0}
/>
</div>
<DrawerFooter>
<Button
onClick={async () => {
console.log(data)
setDisabled(true)
// const payment = await createPayment(data)
setDisabled(false)
// setMonths(1)
// if (payment) {
// router.push(`/payments/${payment.id}`);
// setIsOpen(!isOpen);
// } else {
// toast.error("Something went wrong.")
// }
}}
className="w-full"
disabled={amount === 0 || disabled}
>
{disabled ? (
<>
<Loader2 className="ml-2 animate-spin" />
</>
) : (
<>
Go to payment
<CircleDollarSign />
</>
)}
</Button>
<DrawerClose asChild>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</div>
</DrawerContent>
</Drawer>
);
<div className="px-4 flex flex-col gap-4">
<NumberInput
label="Set amount to top up"
value={amount}
onChange={(value) => setAmount(value)}
maxAllowed={5000}
isDisabled={amount === 0}
/>
</div>
<DrawerFooter>
<Button
onClick={async () => {
console.log(data);
setDisabled(true);
// const payment = await createPayment(data)
setDisabled(false);
// setMonths(1)
// if (payment) {
// router.push(`/payments/${payment.id}`);
// setIsOpen(!isOpen);
// } else {
// toast.error("Something went wrong.")
// }
}}
className="w-full"
disabled={amount === 0 || disabled}
>
{disabled ? (
<>
<Loader2 className="ml-2 animate-spin" />
</>
) : (
<>
Go to payment
<CircleDollarSign />
</>
)}
</Button>
<DrawerClose asChild>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</div>
</DrawerContent>
</Drawer>
);
}