refactor(auth): extract handleSignOut server action into dedicated module to fix client build error
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 5s

This commit is contained in:
Chneemann 2026-08-22 06:56:07 +02:00
parent f3a526a68c
commit 9802febef3
No known key found for this signature in database
5 changed files with 54 additions and 35 deletions

31
actions/auth.actions.ts Normal file
View file

@ -0,0 +1,31 @@
/**
* @file actions/auth.actions.ts
* @description Server actions for handling user logout and status updates.
*/
"use server";
import { auth, signOut } from "@/auth";
import { db } from "@/db";
import { usersTable } from "@/db/schema";
import { eq } from "drizzle-orm";
/**
* Handles user sign-out by updating the user's online status to offline in the database
* and terminating the active session, redirecting to the homepage.
*
* @async
* @returns {Promise<void>} Resolves when the user is signed out and redirected.
*/
export async function handleSignOut() {
const session = await auth();
if (session?.user?.id) {
await db
.update(usersTable)
.set({ isOnline: false })
.where(eq(usersTable.id, session.user.id));
}
await signOut({ redirectTo: "/" });
}

View file

@ -9,6 +9,7 @@ import { useState, useRef, useEffect } from "react";
import Link from "next/link"; import Link from "next/link";
import { MoreHorizontal, X } from "lucide-react"; import { MoreHorizontal, X } from "lucide-react";
import { LEGAL_LINKS, COPYRIGHT_TEXT, ICON_MAP } from "@/utils/legal"; import { LEGAL_LINKS, COPYRIGHT_TEXT, ICON_MAP } from "@/utils/legal";
import SignOutButton from "../ui/buttons/SignOutButton";
/** /**
* Renders a mobile menu button and a slide-up modal overlay containing * Renders a mobile menu button and a slide-up modal overlay containing
@ -130,6 +131,9 @@ export default function MobileLegalMenu() {
</Link> </Link>
); );
})} })}
{/* Sign Out Button */}
<SignOutButton />
</div> </div>
{/* Shared Copyright */} {/* Shared Copyright */}

View file

@ -1,5 +1,5 @@
/** /**
* @file Sidebar.tsx * @file components/layout/Sidebar.tsx
* @description Server/Client component rendering the desktop application sidebar layout including the brand logo, navigation links, and sign-out option. * @description Server/Client component rendering the desktop application sidebar layout including the brand logo, navigation links, and sign-out option.
*/ */
@ -23,9 +23,7 @@ export default function Sidebar() {
</div> </div>
{/* Lower Section */} {/* Lower Section */}
<div className="flex justify-center"> <SignOutButton align="center" />
<SignOutButton />
</div>
</aside> </aside>
); );
} }

View file

@ -1,25 +1,33 @@
/** /**
* @file SignOutButton.tsx * @file components/ui/buttons/SignOutButton.tsx
* @description Server Action-powered client component rendering a sign-out button that triggers session termination via a server action. * @description Server Action-powered client component rendering a sign-out button with customizable alignment.
*/ */
import { handleSignOut } from "@/auth"; import { handleSignOut } from "@/actions/auth.actions";
import { LogOut } from "lucide-react"; import { LogOut } from "lucide-react";
/** /**
* Renders a form containing a submit button to securely sign out the current user session using a server action. * Renders a sign-out button wrapped in a form that invokes the handleSignOut Server Action upon submission.
* *
* @param {Object} props - The component props.
* @param {"left" | "center"} [props.align="left"] - Text and content alignment for the button contents.
* @returns {JSX.Element} The rendered sign-out button component. * @returns {JSX.Element} The rendered sign-out button component.
*/ */
export default function SignOutButton() { export default function SignOutButton({
align = "left",
}: {
align?: "left" | "center";
}) {
return ( return (
<form action={handleSignOut}> <form action={handleSignOut} className="w-full">
<button <button
type="submit" type="submit"
className="flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium hover:text-primary-hover transition-colors w-full cursor-pointer" className={`flex items-center gap-3 w-full px-3 py-2.5 rounded-lg text-sm font-medium text-destructive hover:bg-destructive/10 transition-colors cursor-pointer ${
align === "center" ? "justify-center" : "text-left"
}`}
> >
<LogOut className="w-4 h-4" /> <LogOut className="w-4 h-4 shrink-0" />
<span>Sign out</span> <span>Sign Out</span>
</button> </button>
</form> </form>
); );

22
auth.ts
View file

@ -95,25 +95,3 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
}, },
}, },
}); });
/**
* Handles the secure logout process, setting the user's online status to false in the database
* and destroying the active session.
*
* @async
* @returns {Promise<void>}
*/
export async function handleSignOut() {
"use server";
const session = await auth();
if (session?.user?.id) {
await db
.update(usersTable)
.set({ isOnline: false })
.where(eq(usersTable.id, session.user.id));
}
await signOut({ redirectTo: "/" });
}