mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
feat(cli): add polar as better-auth plugin (#578)
This commit is contained in:
@@ -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}}
|
||||
});
|
||||
|
||||
@@ -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}}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user