feat(cli): add polar as better-auth plugin (#578)

This commit is contained in:
Aman Varshney
2025-09-16 17:53:44 +05:30
committed by GitHub
parent 3f22373cc3
commit ba3d62b6b9
77 changed files with 1221 additions and 308 deletions

View File

@@ -1,4 +1,7 @@
import { createAuthClient } from "better-auth/react";
{{#if (eq payments "polar")}}
import { polarClient } from "@polar-sh/better-auth";
{{/if}}
export const authClient = createAuthClient({
baseURL:
@@ -7,4 +10,7 @@ export const authClient = createAuthClient({
{{else}}
import.meta.env.VITE_SERVER_URL,
{{/if}}
{{#if (eq payments "polar")}}
plugins: [polarClient()]
{{/if}}
});

View File

@@ -0,0 +1,58 @@
"use client";
import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth-client";
{{#if (eq api "orpc")}}
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { useQuery } from "@tanstack/react-query";
import { trpc } from "@/utils/trpc";
{{/if}}
export default function Dashboard({
{{#if (eq payments "polar")}}
customerState,
{{/if}}
session
}: {
{{#if (eq payments "polar")}}
customerState: ReturnType<typeof authClient.customer.state>;
{{/if}}
session: typeof authClient.$Infer.Session;
}) {
{{#if (eq api "orpc")}}
const privateData = useQuery(orpc.privateData.queryOptions());
{{/if}}
{{#if (eq api "trpc")}}
const privateData = useQuery(trpc.privateData.queryOptions());
{{/if}}
{{#if (eq payments "polar")}}
const hasProSubscription = customerState?.activeSubscriptions?.length! > 0;
console.log("Active subscriptions:", customerState?.activeSubscriptions);
{{/if}}
return (
<>
{{#if (eq api "orpc")}}
<p>API: {privateData.data?.message}</p>
{{/if}}
{{#if (eq api "trpc")}}
<p>API: {privateData.data?.message}</p>
{{/if}}
{{#if (eq payments "polar")}}
<p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
{hasProSubscription ? (
<Button onClick={async () => await authClient.customer.portal()}>
Manage Subscription
</Button>
) : (
<Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
Upgrade to Pro
</Button>
)}
{{/if}}
</>
);
}

View File

@@ -1,47 +1,37 @@
"use client"
import { authClient } from "@/lib/auth-client";
{{#if (eq api "orpc")}}
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { useQuery } from "@tanstack/react-query";
import { trpc } from "@/utils/trpc";
{{/if}}
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { redirect } from "next/navigation";
import Dashboard from "./dashboard";
import { headers } from "next/headers";
export default function Dashboard() {
const router = useRouter();
const { data: session, isPending } = authClient.useSession();
export default async function DashboardPage() {
const session = await authClient.getSession({
fetchOptions: {
headers: await headers()
}
});
{{#if (eq api "orpc")}}
const privateData = useQuery(orpc.privateData.queryOptions());
{{/if}}
{{#if (eq api "trpc")}}
const privateData = useQuery(trpc.privateData.queryOptions());
{{/if}}
if (!session.data) {
redirect("/login");
}
useEffect(() => {
if (!session && !isPending) {
router.push("/login");
}
}, [session, isPending]);
{{#if (eq payments "polar")}}
const { data: customerState, error } = await authClient.customer.state({
fetchOptions: {
headers: await headers()
}
});
{{/if}}
if (isPending) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome {session?.user.name}</p>
{{#if (eq api "orpc")}}
<p>privateData: {privateData.data?.message}</p>
{{/if}}
{{#if (eq api "trpc")}}
<p>privateData: {privateData.data?.message}</p>
{{/if}}
</div>
);
return (
<div>
<h1>Dashboard</h1>
<p>Welcome {session.data.user.name}</p>
<Dashboard
session={session.data}
{{#if (eq payments "polar")}}
customerState={customerState}
{{/if}}
/>
</div>
);
}

View File

@@ -1,3 +1,4 @@
import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth-client";
{{#if (eq api "orpc")}}
import { orpc } from "@/utils/orpc";
@@ -8,12 +9,15 @@ import { trpc } from "@/utils/trpc";
{{#if ( or (eq api "orpc") (eq api "trpc"))}}
import { useQuery } from "@tanstack/react-query";
{{/if}}
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router";
export default function Dashboard() {
const { data: session, isPending } = authClient.useSession();
const navigate = useNavigate();
{{#if (eq payments "polar")}}
const [customerState, setCustomerState] = useState<any>(null);
{{/if}}
{{#if (eq api "orpc")}}
const privateData = useQuery(orpc.privateData.queryOptions());
@@ -26,18 +30,48 @@ export default function Dashboard() {
if (!session && !isPending) {
navigate("/login");
}
}, [session, isPending]);
}, [session, isPending, navigate]);
{{#if (eq payments "polar")}}
useEffect(() => {
async function fetchCustomerState() {
if (session) {
const { data } = await authClient.customer.state();
setCustomerState(data);
}
}
fetchCustomerState();
}, [session]);
{{/if}}
if (isPending) {
return <div>Loading...</div>;
}
{{#if (eq payments "polar")}}
const hasProSubscription = customerState?.activeSubscriptions?.length! > 0;
console.log("Active subscriptions:", customerState?.activeSubscriptions);
{{/if}}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome {session?.user.name}</p>
{{#if ( or (eq api "orpc") (eq api "trpc"))}}
<p>privateData: {privateData.data?.message}</p>
<p>API: {privateData.data?.message}</p>
{{/if}}
{{#if (eq payments "polar")}}
<p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
{hasProSubscription ? (
<Button onClick={async () => await authClient.customer.portal()}>
Manage Subscription
</Button>
) : (
<Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
Upgrade to Pro
</Button>
)}
{{/if}}
</div>
);

View File

@@ -1,3 +1,4 @@
import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth-client";
{{#if (eq api "orpc")}}
import { orpc } from "@/utils/orpc";
@@ -8,44 +9,61 @@ import { trpc } from "@/utils/trpc";
{{#if ( or (eq api "orpc") (eq api "trpc"))}}
import { useQuery } from "@tanstack/react-query";
{{/if}}
import { createFileRoute } from "@tanstack/react-router";
import { useEffect } from "react";
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute("/dashboard")({
component: RouteComponent,
component: RouteComponent,
beforeLoad: async () => {
const session = await authClient.getSession();
if (!session.data) {
redirect({
to: "/login",
throw: true
});
}
{{#if (eq payments "polar")}}
const {data: customerState} = await authClient.customer.state()
return { session, customerState };
{{else}}
return { session };
{{/if}}
}
});
function RouteComponent() {
const { data: session, isPending } = authClient.useSession();
const { session{{#if (eq payments "polar")}}, customerState{{/if}} } = Route.useRouteContext();
const navigate = Route.useNavigate();
{{#if (eq api "orpc")}}
const privateData = useQuery(orpc.privateData.queryOptions());
{{/if}}
{{#if (eq api "trpc")}}
const privateData = useQuery(trpc.privateData.queryOptions());
{{/if}}
{{#if (eq api "orpc")}}
const privateData = useQuery(orpc.privateData.queryOptions());
{{/if}}
{{#if (eq api "trpc")}}
const privateData = useQuery(trpc.privateData.queryOptions());
{{/if}}
{{#if (eq payments "polar")}}
const hasProSubscription = customerState?.activeSubscriptions?.length! > 0
console.log("Active subscriptions:", customerState?.activeSubscriptions)
{{/if}}
useEffect(() => {
if (!session && !isPending) {
navigate({
to: "/login",
});
}
}, [session, isPending]);
if (isPending) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome {session?.user.name}</p>
{{#if ( or (eq api "orpc") (eq api "trpc"))}}
<p>privateData: {privateData.data?.message}</p>
{{/if}}
</div>
);
return (
<div>
<h1>Dashboard</h1>
<p>Welcome {session.data?.user.name}</p>
{{#if ( or (eq api "orpc") (eq api "trpc"))}}
<p>API: {privateData.data?.message}</p>
{{/if}}
{{#if (eq payments "polar")}}
<p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
{hasProSubscription ? (
<Button onClick={async () => await authClient.customer.portal()}>
Manage Subscription
</Button>
) : (
<Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
Upgrade to Pro
</Button>
)}
{{/if}}
</div>
);
}

View File

@@ -1,3 +1,4 @@
import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth-client";
{{#if (eq api "trpc")}}
import { useTRPC } from "@/utils/trpc";
@@ -7,48 +8,62 @@ import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/utils/orpc";
import { useQuery } from "@tanstack/react-query";
{{/if}}
import { createFileRoute } from "@tanstack/react-router";
import { useEffect } from "react";
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute("/dashboard")({
component: RouteComponent,
component: RouteComponent,
beforeLoad: async () => {
const session = await authClient.getSession();
if (!session.data) {
redirect({
to: "/login",
throw: true
});
}
{{#if (eq payments "polar")}}
const {data: customerState} = await authClient.customer.state()
return { session, customerState };
{{else}}
return { session };
{{/if}}
}
});
function RouteComponent() {
const navigate = Route.useNavigate();
{{#if (eq api "trpc")}}
const trpc = useTRPC();
{{/if}}
{{#if (eq api "orpc")}}
{{/if}}
const { data: session, isPending } = authClient.useSession();
const { session{{#if (eq payments "polar")}}, customerState{{/if}} } = Route.useRouteContext();
{{#if (eq api "trpc")}}
const privateData = useQuery(trpc.privateData.queryOptions());
{{/if}}
{{#if (eq api "orpc")}}
const privateData = useQuery(orpc.privateData.queryOptions());
{{/if}}
{{#if (eq api "trpc")}}
const trpc = useTRPC();
const privateData = useQuery(trpc.privateData.queryOptions());
{{/if}}
{{#if (eq api "orpc")}}
const privateData = useQuery(orpc.privateData.queryOptions());
{{/if}}
useEffect(() => {
if (!session && !isPending) {
navigate({
to: "/login",
});
}
}, [session, isPending]);
{{#if (eq payments "polar")}}
const hasProSubscription = customerState?.activeSubscriptions?.length! > 0
console.log("Active subscriptions:", customerState?.activeSubscriptions)
{{/if}}
if (isPending) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome {session?.user.name}</p>
{{#if ( or (eq api "orpc") (eq api "trpc"))}}
<p>privateData: {privateData.data?.message}</p>
{{/if}}
</div>
);
return (
<div>
<h1>Dashboard</h1>
<p>Welcome {session.data?.user.name}</p>
{{#if ( or (eq api "orpc") (eq api "trpc"))}}
<p>API: {privateData.data?.message}</p>
{{/if}}
{{#if (eq payments "polar")}}
<p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
{hasProSubscription ? (
<Button onClick={async () => await authClient.customer.portal()}>
Manage Subscription
</Button>
) : (
<Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
Upgrade to Pro
</Button>
)}
{{/if}}
</div>
);
}