Patching bug on portabase.config.ts about social providers.

This commit is contained in:
charlesgauthereau
2025-12-30 10:27:47 +01:00
parent ff175e1edd
commit 011120fdf9
9 changed files with 52 additions and 73 deletions
+3 -7
View File
@@ -24,18 +24,16 @@ export default async function SignInPage() {
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<LoginForm /> <LoginForm/>
{SUPPORTED_PROVIDERS.filter((p) => !p.isManual).length > 0 && ( {SUPPORTED_PROVIDERS.filter((p) => !p.isManual && p.isActive).length > 0 && (
<> <>
<div className="relative my-4 flex items-center justify-center overflow-hidden"> <div className="relative my-4 flex items-center justify-center overflow-hidden">
<Separator/> <Separator/>
<div className="px-2 text-center text-sm">OR</div> <div className="px-2 text-center text-sm">OR</div>
<Separator/> <Separator/>
</div> </div>
<SocialAuthButtons providers={SUPPORTED_PROVIDERS}/>
<SocialAuthButtons />
</> </>
)} )}
@@ -50,7 +48,5 @@ export default async function SignInPage() {
</TooltipProvider> </TooltipProvider>
) )
} }
+6 -4
View File
@@ -1,9 +1,9 @@
import {Chrome, KeyRound, LucideIcon} from "lucide-react";
export interface AuthProviderConfig { export interface AuthProviderConfig {
id: "google" | "github" | "credential"; id: "google" | "github" | "credential";
icon: LucideIcon; isActive: boolean;
icon: string;
isManual?: boolean; isManual?: boolean;
credentials?: { credentials?: {
clientId: string; clientId: string;
@@ -80,7 +80,8 @@ export const PORTABASE_DEFAULT_SETTINGS = {
export const SUPPORTED_PROVIDERS: AuthProviderConfig[] = [ export const SUPPORTED_PROVIDERS: AuthProviderConfig[] = [
{ {
id: "google", id: "google",
icon: Chrome, icon: "hugeicons:chrome",
isActive: Boolean(process.env.AUTH_GOOGLE_METHOD),
// isManual: true, // isManual: true,
credentials: { credentials: {
clientId: process.env.GOOGLE_CLIENT_ID || "", clientId: process.env.GOOGLE_CLIENT_ID || "",
@@ -100,7 +101,8 @@ export const SUPPORTED_PROVIDERS: AuthProviderConfig[] = [
},*/ },*/
{ {
id: "credential", id: "credential",
icon: KeyRound, isActive: true,
icon: "proicons:key",
isManual: true, isManual: true,
credentials: { credentials: {
clientId: "", clientId: "",
@@ -5,9 +5,12 @@ import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth/auth-client"; import { authClient } from "@/lib/auth/auth-client";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import {SUPPORTED_PROVIDERS} from "../../../../portabase.config"; import {AuthProviderConfig} from "../../../../portabase.config";
import {Icon} from "@iconify/react";
export function SocialAuthButtons({ providers }: { providers: AuthProviderConfig[] }) {
const socialProviders = providers.filter(p => p.isActive && !p.isManual);
export function SocialAuthButtons() {
const [isLoading, setIsLoading] = useState<string | null>(null); const [isLoading, setIsLoading] = useState<string | null>(null);
const handleSocialSignIn = async (providerId: string) => { const handleSocialSignIn = async (providerId: string) => {
@@ -30,16 +33,15 @@ export function SocialAuthButtons() {
} }
}; };
const socialProviders = SUPPORTED_PROVIDERS.filter((p) => !p.isManual);
if (socialProviders.length === 0) return null; if (socialProviders.length === 0) return null;
return ( return (
<div className="flex flex-col gap-2 w-full"> <div className="flex flex-col gap-2 w-full">
{socialProviders.map((provider) => ( {socialProviders.map((provider) => (
<Button key={provider.id} variant="outline" className="w-full gap-2" onClick={() => handleSocialSignIn(provider.id)} disabled={!!isLoading}> <Button key={provider.id} variant="outline" className="w-full gap-2" onClick={() => handleSocialSignIn(provider.id)} disabled={!!isLoading}>
{isLoading === provider.id ? <Loader2 className="h-4 w-4 animate-spin" /> : <provider.icon className="h-4 w-4" />} {isLoading === provider.id ? <Loader2 className="h-4 w-4 animate-spin" /> :
<Icon icon={provider.icon} className="h-4 w-4"/>
}
<span>{PROVIDERS_TEXT[provider.id].title}</span> <span>{PROVIDERS_TEXT[provider.id].title}</span>
</Button> </Button>
))} ))}
@@ -1,6 +1,7 @@
import {currentUser} from "@/lib/auth/current-user"; import {currentUser} from "@/lib/auth/current-user";
import {getAccounts, getSession, getSessions} from "@/lib/auth/auth"; import {getAccounts, getSession, getSessions} from "@/lib/auth/auth";
import {LoggedInButtonClient} from "./logged-in-button"; import {LoggedInButtonClient} from "./logged-in-button";
import {SUPPORTED_PROVIDERS} from "../../../../../../portabase.config";
export const LoggedInButton = async () => { export const LoggedInButton = async () => {
const user = await currentUser(); const user = await currentUser();
@@ -18,6 +19,7 @@ export const LoggedInButton = async () => {
// @ts-ignore // @ts-ignore
currentSession={currentSession.session} currentSession={currentSession.session}
accounts={accounts} accounts={accounts}
providers={SUPPORTED_PROVIDERS.filter((p) => p.isActive)}
/> />
); );
}; };
@@ -1,46 +1,3 @@
// import { ChevronUp } from "lucide-react";
//
// import {Avatar, AvatarFallback, AvatarImage} from "@/components/ui/avatar";
// import { currentUser } from "@/lib/auth/current-user";
// import { SidebarMenuButton } from "@/components/ui/sidebar";
// import { getAccounts, getSession, getSessions } from "@/lib/auth/auth";
// import {LoggedInDropdown} from "@/components/wrappers/dashboard/common/logged-in/logged-in-dropdown";
//
// export const LoggedInButton = async () => {
// const user = await currentUser();
// const sessions = await getSessions();
// const currentSession = await getSession();
// const accounts = await getAccounts();
//
// if (!user) return null;
//
//
// return (
// <>
// <LoggedInDropdown
// // @ts-ignore
// user={user}
// // @ts-ignore
// sessions={sessions}
// // @ts-ignore
// currentSession={currentSession.session}
// // @ts-ignore
// accounts={accounts}
// >
// <SidebarMenuButton>
//
// <Avatar className="size-6">
// <AvatarFallback>{user?.name[0].toUpperCase()}</AvatarFallback>
// {user?.image ? <AvatarImage src={user?.image} alt={`${user.name ?? "-"}'s profile picture`} /> : null}
// </Avatar>
//
// <span className="first-letter:capitalize">{user?.name}</span>
// <ChevronUp className="ml-auto" />
// </SidebarMenuButton>
// </LoggedInDropdown>
// </>
// );
// };
"use client"; "use client";
import {ChevronUp} from "lucide-react"; import {ChevronUp} from "lucide-react";
@@ -48,15 +5,23 @@ import {Avatar, AvatarFallback, AvatarImage} from "@/components/ui/avatar";
import {SidebarMenuButton} from "@/components/ui/sidebar"; import {SidebarMenuButton} from "@/components/ui/sidebar";
import {LoggedInDropdown} from "./logged-in-dropdown"; import {LoggedInDropdown} from "./logged-in-dropdown";
import {Account, Session, User} from "better-auth"; import {Account, Session, User} from "better-auth";
import {AuthProviderConfig} from "../../../../../../portabase.config";
type LoggedInButtonClientProps = { type LoggedInButtonClientProps = {
user: User; user: User,
sessions: Session[]; sessions: Session[],
currentSession: Session; currentSession: Session,
accounts: Account[]; accounts: Account[],
providers: AuthProviderConfig[]
} }
export const LoggedInButtonClient = ({user, sessions, currentSession, accounts}: LoggedInButtonClientProps) => { export const LoggedInButtonClient = ({
user,
sessions,
currentSession,
accounts,
providers
}: LoggedInButtonClientProps) => {
return ( return (
<LoggedInDropdown <LoggedInDropdown
@@ -68,6 +33,7 @@ export const LoggedInButtonClient = ({user, sessions, currentSession, accounts}:
currentSession={currentSession} currentSession={currentSession}
// @ts-ignore // @ts-ignore
accounts={accounts} accounts={accounts}
providers={providers}
> >
<SidebarMenuButton type="button"> <SidebarMenuButton type="button">
<Avatar className="size-6"> <Avatar className="size-6">
@@ -7,6 +7,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
import { signOut } from "@/lib/auth/auth-client"; import { signOut } from "@/lib/auth/auth-client";
import {ProfileModal} from "@/components/wrappers/dashboard/common/profile/profile-modal"; import {ProfileModal} from "@/components/wrappers/dashboard/common/profile/profile-modal";
import {Account, Session, User} from "@/db/schema/02_user"; import {Account, Session, User} from "@/db/schema/02_user";
import {AuthProviderConfig} from "../../../../../../portabase.config";
export type LoggedInDropdownProps = PropsWithChildren<{ export type LoggedInDropdownProps = PropsWithChildren<{
user: User; user: User;
@@ -14,9 +15,11 @@ export type LoggedInDropdownProps = PropsWithChildren<{
currentSession: Session; currentSession: Session;
accounts: Account[]; accounts: Account[];
children: ReactNode; children: ReactNode;
providers: AuthProviderConfig[]
}>; }>;
export const LoggedInDropdown = ({ user, sessions, currentSession, accounts, children }: LoggedInDropdownProps) => { export const LoggedInDropdown = ({ user, sessions, currentSession, accounts, children, providers }: LoggedInDropdownProps) => {
const router = useRouter(); const router = useRouter();
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
@@ -29,6 +32,7 @@ export const LoggedInDropdown = ({ user, sessions, currentSession, accounts, chi
accounts={accounts} accounts={accounts}
open={isModalOpen} open={isModalOpen}
onOpenChange={setIsModalOpen} onOpenChange={setIsModalOpen}
providers={providers}
/> />
<DropdownMenu> <DropdownMenu>
@@ -10,6 +10,7 @@ import {ProfileAccount} from "@/components/wrappers/dashboard/profile/profile-ac
import {ProfileAppearance} from "@/components/wrappers/dashboard/profile/profile-apperance"; import {ProfileAppearance} from "@/components/wrappers/dashboard/profile/profile-apperance";
import {ProfileSecurity} from "@/components/wrappers/dashboard/profile/profile-security"; import {ProfileSecurity} from "@/components/wrappers/dashboard/profile/profile-security";
import {ProfileGeneral} from "@/components/wrappers/dashboard/profile/profile-general"; import {ProfileGeneral} from "@/components/wrappers/dashboard/profile/profile-general";
import {AuthProviderConfig} from "../../../../../../portabase.config";
type ProfileModalProps = { type ProfileModalProps = {
open: boolean; open: boolean;
@@ -18,9 +19,11 @@ type ProfileModalProps = {
currentSession: Session; currentSession: Session;
accounts: Account[]; accounts: Account[];
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
providers: AuthProviderConfig[]
}; };
export const ProfileModal = ({ user, sessions, currentSession, accounts, open, onOpenChange }: ProfileModalProps) => { export const ProfileModal = ({ user, sessions, currentSession, accounts, open, onOpenChange, providers }: ProfileModalProps) => {
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="w-[95vw] h-[90vh] max-w-md lg:max-w-[1000px] lg:h-[800px] p-0 overflow-hidden flex flex-col outline-none gap-0 rounded-xl bg-background"> <DialogContent className="w-[95vw] h-[90vh] max-w-md lg:max-w-[1000px] lg:h-[800px] p-0 overflow-hidden flex flex-col outline-none gap-0 rounded-xl bg-background">
@@ -46,7 +49,7 @@ export const ProfileModal = ({ user, sessions, currentSession, accounts, open, o
</TabsContent> </TabsContent>
<TabsContent value="providers" className="mt-0 h-full p-6 lg:p-10 outline-none focus-visible:ring-0"> <TabsContent value="providers" className="mt-0 h-full p-6 lg:p-10 outline-none focus-visible:ring-0">
<ProfileProviders accounts={accounts} /> <ProfileProviders accounts={accounts} providers={providers} />
</TabsContent> </TabsContent>
<TabsContent value="account" className="mt-0 h-full p-6 lg:p-10 outline-none focus-visible:ring-0"> <TabsContent value="account" className="mt-0 h-full p-6 lg:p-10 outline-none focus-visible:ring-0">
@@ -12,13 +12,16 @@ import {useRouter} from "next/navigation";
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip"; import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
import {Alert, AlertDescription} from "@/components/ui/alert"; import {Alert, AlertDescription} from "@/components/ui/alert";
import {SetPasswordProfileProviderModal} from "./modal/set-password-modal"; import {SetPasswordProfileProviderModal} from "./modal/set-password-modal";
import {SUPPORTED_PROVIDERS} from "../../../../../portabase.config"; import {AuthProviderConfig} from "../../../../../portabase.config";
import {Icon} from "@iconify/react";
interface ProfileProviderProps { interface ProfileProviderProps {
accounts: Account[]; accounts: Account[];
providers: AuthProviderConfig[]
} }
export function ProfileProviders({accounts}: ProfileProviderProps) { export function ProfileProviders({accounts, providers}: ProfileProviderProps) {
const router = useRouter(); const router = useRouter();
const totalConnected = accounts.length; const totalConnected = accounts.length;
const [loadingProvider, setLoadingProvider] = useState<string | null>(null); const [loadingProvider, setLoadingProvider] = useState<string | null>(null);
@@ -70,7 +73,7 @@ export function ProfileProviders({accounts}: ProfileProviderProps) {
</div> </div>
<div className="grid gap-4"> <div className="grid gap-4">
{SUPPORTED_PROVIDERS.map((provider) => { {providers.map((provider) => {
const linkedAccount = accounts.find((acc) => acc.providerId === provider.id); const linkedAccount = accounts.find((acc) => acc.providerId === provider.id);
const isConnected = !!linkedAccount; const isConnected = !!linkedAccount;
// const canUnlink = totalConnected > 1 || (totalConnected === 1 && !provider.isManual); // const canUnlink = totalConnected > 1 || (totalConnected === 1 && !provider.isManual);
@@ -83,7 +86,7 @@ export function ProfileProviders({accounts}: ProfileProviderProps) {
className="flex items-center justify-between p-4 border rounded-lg hover:bg-muted/30 transition-colors"> className="flex items-center justify-between p-4 border rounded-lg hover:bg-muted/30 transition-colors">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="w-10 h-10 rounded-full bg-muted flex items-center justify-center"> <div className="w-10 h-10 rounded-full bg-muted flex items-center justify-center">
<provider.icon className="w-5 h-5"/> <Icon icon={provider.icon} className="w-5 h-5"/>
</div> </div>
<div className="space-y-0.5"> <div className="space-y-0.5">
<div className="font-medium flex items-center gap-2"> <div className="font-medium flex items-center gap-2">
+2 -1
View File
@@ -11,7 +11,7 @@ import {count, eq} from "drizzle-orm";
import {MemberWithUser, OrganizationWithMembersAndUsers} from "@/db/schema/03_organization"; import {MemberWithUser, OrganizationWithMembersAndUsers} from "@/db/schema/03_organization";
import {sendEmail} from "@/lib/email/email-helper"; import {sendEmail} from "@/lib/email/email-helper";
import {render} from "@react-email/render"; import {render} from "@react-email/render";
import {SUPPORTED_PROVIDERS} from "../../../portabase.config"; import {AuthProviderConfig, SUPPORTED_PROVIDERS} from "../../../portabase.config";
import {withUpdatedAt} from "@/db/utils"; import {withUpdatedAt} from "@/db/utils";
import EmailVerification from "@/components/emails/auth/email-verification"; import EmailVerification from "@/components/emails/auth/email-verification";
import EmailForgotPassword from "@/components/emails/auth/email-forgot-password"; import EmailForgotPassword from "@/components/emails/auth/email-forgot-password";
@@ -80,6 +80,7 @@ export const auth = betterAuth({
}, },
}, },
socialProviders: SUPPORTED_PROVIDERS.reduce((acc: any, provider: any) => { socialProviders: SUPPORTED_PROVIDERS.reduce((acc: any, provider: any) => {
if (!provider.isActive) return acc;
if (provider.id === "credential") return acc; if (provider.id === "credential") return acc;
if (provider.id === "google") { if (provider.id === "google") {
acc.google = { acc.google = {