create-better-t-stack 2.45.5 → 2.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.js +1 -1
  4. package/dist/{src-Cun9EO6e.js → src-NOw0j6Z9.js} +272 -132
  5. package/package.json +1 -1
  6. package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +153 -6
  7. package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +36 -2
  8. package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts.hbs +22 -0
  9. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +6 -0
  10. package/templates/auth/better-auth/web/react/next/src/app/dashboard/dashboard.tsx.hbs +58 -0
  11. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +31 -41
  12. package/templates/auth/better-auth/web/react/react-router/src/routes/dashboard.tsx.hbs +37 -3
  13. package/templates/auth/better-auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +50 -32
  14. package/templates/auth/better-auth/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +51 -36
  15. package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts.hbs +11 -0
  16. package/templates/auth/better-auth/web/solid/src/routes/dashboard.tsx.hbs +52 -29
  17. package/templates/auth/better-auth/web/svelte/src/lib/{auth-client.ts → auth-client.ts.hbs} +6 -0
  18. package/templates/auth/better-auth/web/svelte/src/routes/dashboard/+page.svelte.hbs +24 -3
  19. package/templates/backend/server/server-base/package.json.hbs +1 -1
  20. package/templates/extras/bunfig.toml.hbs +4 -0
  21. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +4 -0
  22. package/templates/frontend/solid/package.json.hbs +10 -10
  23. package/templates/payments/polar/server/base/src/lib/payments.ts.hbs +6 -0
  24. package/templates/payments/polar/web/nuxt/app/pages/success.vue.hbs +11 -0
  25. package/templates/payments/polar/web/react/next/src/app/success/page.tsx.hbs +15 -0
  26. package/templates/payments/polar/web/react/react-router/src/routes/success.tsx.hbs +13 -0
  27. package/templates/payments/polar/web/react/tanstack-router/src/routes/success.tsx.hbs +19 -0
  28. package/templates/payments/polar/web/react/tanstack-start/src/routes/success.tsx.hbs +19 -0
  29. package/templates/payments/polar/web/solid/src/routes/success.tsx.hbs +23 -0
  30. package/templates/payments/polar/web/svelte/src/routes/success/+page.svelte.hbs +12 -0
  31. package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts +0 -16
  32. package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts +0 -5
  33. /package/templates/auth/better-auth/web/nuxt/app/components/{SignInForm.vue → SignInForm.vue.hbs} +0 -0
  34. /package/templates/auth/better-auth/web/nuxt/app/components/{SignUpForm.vue → SignUpForm.vue.hbs} +0 -0
  35. /package/templates/auth/better-auth/web/nuxt/app/components/{UserMenu.vue → UserMenu.vue.hbs} +0 -0
  36. /package/templates/auth/better-auth/web/nuxt/app/pages/{login.vue → login.vue.hbs} +0 -0
  37. /package/templates/auth/better-auth/web/react/next/src/app/login/{page.tsx → page.tsx.hbs} +0 -0
  38. /package/templates/auth/better-auth/web/react/next/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  39. /package/templates/auth/better-auth/web/react/next/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  40. /package/templates/auth/better-auth/web/react/next/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  41. /package/templates/auth/better-auth/web/react/next/src/components/{user-menu.tsx → user-menu.tsx.hbs} +0 -0
  42. /package/templates/auth/better-auth/web/react/react-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  43. /package/templates/auth/better-auth/web/react/react-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  44. /package/templates/auth/better-auth/web/react/react-router/src/components/{user-menu.tsx → user-menu.tsx.hbs} +0 -0
  45. /package/templates/auth/better-auth/web/react/react-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  46. /package/templates/auth/better-auth/web/svelte/src/components/{SignInForm.svelte → SignInForm.svelte.hbs} +0 -0
  47. /package/templates/auth/better-auth/web/svelte/src/components/{SignUpForm.svelte → SignUpForm.svelte.hbs} +0 -0
  48. /package/templates/auth/better-auth/web/svelte/src/components/{UserMenu.svelte → UserMenu.svelte.hbs} +0 -0
  49. /package/templates/auth/better-auth/web/svelte/src/routes/login/{+page.svelte → +page.svelte.hbs} +0 -0
@@ -4,6 +4,10 @@ import { prismaAdapter } from "better-auth/adapters/prisma";
4
4
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
5
5
  import { expo } from "@better-auth/expo";
6
6
  {{/if}}
7
+ {{#if (eq payments "polar")}}
8
+ import { polar, checkout, portal } from "@polar-sh/better-auth";
9
+ import { polarClient } from "./payments";
10
+ {{/if}}
7
11
  import prisma from "../db";
8
12
 
9
13
  export const auth = betterAuth<BetterAuthOptions>({
@@ -28,9 +32,35 @@ export const auth = betterAuth<BetterAuthOptions>({
28
32
  secure: true,
29
33
  httpOnly: true,
30
34
  },
31
- }
35
+ },
36
+ {{#if (eq payments "polar")}}
37
+ plugins: [
38
+ polar({
39
+ client: polarClient,
40
+ createCustomerOnSignUp: true,
41
+ enableCustomerPortal: true,
42
+ use: [
43
+ checkout({
44
+ products: [
45
+ {
46
+ productId: "your-product-id",
47
+ slug: "pro",
48
+ },
49
+ ],
50
+ successUrl: process.env.POLAR_SUCCESS_URL,
51
+ authenticatedUsersOnly: true,
52
+ }),
53
+ portal(),
54
+ ],
55
+ }),
56
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
57
+ expo(),
58
+ {{/if}}
59
+ ],
60
+ {{else}}
32
61
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
33
- , plugins: [expo()]
62
+ plugins: [expo()],
63
+ {{/if}}
34
64
  {{/if}}
35
65
  });
36
66
  {{/if}}
@@ -42,6 +72,10 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle";
42
72
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
43
73
  import { expo } from "@better-auth/expo";
44
74
  {{/if}}
75
+ {{#if (eq payments "polar")}}
76
+ import { polar, checkout, portal } from "@polar-sh/better-auth";
77
+ import { polarClient } from "./payments";
78
+ {{/if}}
45
79
  import { db } from "../db";
46
80
  import * as schema from "../db/schema/auth";
47
81
 
@@ -68,9 +102,35 @@ export const auth = betterAuth<BetterAuthOptions>({
68
102
  httpOnly: true,
69
103
  },
70
104
  },
105
+ {{#if (eq payments "polar")}}
106
+ plugins: [
107
+ polar({
108
+ client: polarClient,
109
+ createCustomerOnSignUp: true,
110
+ enableCustomerPortal: true,
111
+ use: [
112
+ checkout({
113
+ products: [
114
+ {
115
+ productId: "your-product-id",
116
+ slug: "pro",
117
+ },
118
+ ],
119
+ successUrl: process.env.POLAR_SUCCESS_URL,
120
+ authenticatedUsersOnly: true,
121
+ }),
122
+ portal(),
123
+ ],
124
+ }),
125
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
126
+ expo(),
127
+ {{/if}}
128
+ ],
129
+ {{else}}
71
130
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
72
131
  plugins: [expo()],
73
132
  {{/if}}
133
+ {{/if}}
74
134
  });
75
135
  {{/if}}
76
136
 
@@ -80,6 +140,10 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle";
80
140
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
81
141
  import { expo } from "@better-auth/expo";
82
142
  {{/if}}
143
+ {{#if (eq payments "polar")}}
144
+ import { polar, checkout, portal } from "@polar-sh/better-auth";
145
+ import { polarClient } from "./payments";
146
+ {{/if}}
83
147
  import { db } from "../db";
84
148
  import * as schema from "../db/schema/auth";
85
149
  import { env } from "cloudflare:workers";
@@ -109,9 +173,32 @@ export const auth = betterAuth<BetterAuthOptions>({
109
173
  httpOnly: true,
110
174
  },
111
175
  },
176
+ {{#if (eq payments "polar")}}
177
+ plugins: [
178
+ polar({
179
+ client: polarClient,
180
+ createCustomerOnSignUp: true,
181
+ enableCustomerPortal: true,
182
+ use: [
183
+ checkout({
184
+ products: [
185
+ {
186
+ productId: "your-product-id",
187
+ slug: "pro",
188
+ },
189
+ ],
190
+ successUrl: env.POLAR_SUCCESS_URL,
191
+ authenticatedUsersOnly: true,
192
+ }),
193
+ portal(),
194
+ ],
195
+ }),
196
+ ],
197
+ {{else}}
112
198
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
113
199
  plugins: [expo()],
114
200
  {{/if}}
201
+ {{/if}}
115
202
  });
116
203
  {{/if}}
117
204
  {{/if}}
@@ -122,6 +209,10 @@ import { mongodbAdapter } from "better-auth/adapters/mongodb";
122
209
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
123
210
  import { expo } from "@better-auth/expo";
124
211
  {{/if}}
212
+ {{#if (eq payments "polar")}}
213
+ import { polar, checkout, portal } from "@polar-sh/better-auth";
214
+ import { polarClient } from "./payments";
215
+ {{/if}}
125
216
  import { client } from "../db";
126
217
 
127
218
  export const auth = betterAuth<BetterAuthOptions>({
@@ -141,9 +232,35 @@ export const auth = betterAuth<BetterAuthOptions>({
141
232
  secure: true,
142
233
  httpOnly: true,
143
234
  },
144
- }
235
+ },
236
+ {{#if (eq payments "polar")}}
237
+ plugins: [
238
+ polar({
239
+ client: polarClient,
240
+ createCustomerOnSignUp: true,
241
+ enableCustomerPortal: true,
242
+ use: [
243
+ checkout({
244
+ products: [
245
+ {
246
+ productId: "your-product-id",
247
+ slug: "pro",
248
+ },
249
+ ],
250
+ successUrl: process.env.POLAR_SUCCESS_URL,
251
+ authenticatedUsersOnly: true,
252
+ }),
253
+ portal(),
254
+ ],
255
+ }),
256
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
257
+ expo(),
258
+ {{/if}}
259
+ ],
260
+ {{else}}
145
261
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
146
- , plugins: [expo()]
262
+ plugins: [expo()],
263
+ {{/if}}
147
264
  {{/if}}
148
265
  });
149
266
  {{/if}}
@@ -153,6 +270,10 @@ import { betterAuth, type BetterAuthOptions } from "better-auth";
153
270
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
154
271
  import { expo } from "@better-auth/expo";
155
272
  {{/if}}
273
+ {{#if (eq payments "polar")}}
274
+ import { polar, checkout, portal } from "@polar-sh/better-auth";
275
+ import { polarClient } from "./payments";
276
+ {{/if}}
156
277
 
157
278
  export const auth = betterAuth<BetterAuthOptions>({
158
279
  database: "", // Invalid configuration
@@ -171,9 +292,35 @@ export const auth = betterAuth<BetterAuthOptions>({
171
292
  secure: true,
172
293
  httpOnly: true,
173
294
  },
174
- }
295
+ },
296
+ {{#if (eq payments "polar")}}
297
+ plugins: [
298
+ polar({
299
+ client: polarClient,
300
+ createCustomerOnSignUp: true,
301
+ enableCustomerPortal: true,
302
+ use: [
303
+ checkout({
304
+ products: [
305
+ {
306
+ productId: "your-product-id",
307
+ slug: "pro",
308
+ },
309
+ ],
310
+ successUrl: process.env.POLAR_SUCCESS_URL,
311
+ authenticatedUsersOnly: true,
312
+ }),
313
+ portal(),
314
+ ],
315
+ }),
316
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
317
+ expo(),
318
+ {{/if}}
319
+ ],
320
+ {{else}}
175
321
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
176
- , plugins: [expo()]
322
+ plugins: [expo()],
323
+ {{/if}}
177
324
  {{/if}}
178
325
  });
179
326
  {{/if}}
@@ -11,9 +11,28 @@ definePageMeta({
11
11
  const { $orpc } = useNuxtApp()
12
12
 
13
13
  const session = $authClient.useSession()
14
+ {{#if (eq payments "polar")}}
15
+ const customerState = ref<any>(null)
16
+ {{/if}}
14
17
 
15
18
  {{#if (eq api "orpc")}}
16
- const privateData = useQuery($orpc.privateData.queryOptions())
19
+ const privateData = useQuery({
20
+ ...$orpc.privateData.queryOptions(),
21
+ enabled: computed(() => !!session.value?.data?.user)
22
+ })
23
+ {{/if}}
24
+
25
+ {{#if (eq payments "polar")}}
26
+ onMounted(async () => {
27
+ if (session.value?.data) {
28
+ const { data } = await $authClient.customer.state()
29
+ customerState.value = data
30
+ }
31
+ })
32
+
33
+ const hasProSubscription = computed(() =>
34
+ customerState.value?.activeSubscriptions?.length! > 0
35
+ )
17
36
  {{/if}}
18
37
 
19
38
  </script>
@@ -27,7 +46,22 @@ const privateData = useQuery($orpc.privateData.queryOptions())
27
46
  {{#if (eq api "orpc")}}
28
47
  <div v-if="privateData.status.value === 'pending'">Loading private data...</div>
29
48
  <div v-else-if="privateData.status.value === 'error'">Error loading private data: \{{ privateData.error.value?.message }}</div>
30
- <p v-else-if="privateData.data.value">Private Data: \{{ privateData.data.value.message }}</p>
49
+ <p v-else-if="privateData.data.value">API: \{{ privateData.data.value.message }}</p>
50
+ {{/if}}
51
+ {{#if (eq payments "polar")}}
52
+ <p class="mb-2">Plan: \{{ hasProSubscription ? "Pro" : "Free" }}</p>
53
+ <UButton
54
+ v-if="hasProSubscription"
55
+ @click="() => { $authClient.customer.portal() }"
56
+ >
57
+ Manage Subscription
58
+ </UButton>
59
+ <UButton
60
+ v-else
61
+ @click="() => { $authClient.checkout({ slug: 'pro' }) }"
62
+ >
63
+ Upgrade to Pro
64
+ </UButton>
31
65
  {{/if}}
32
66
  </div>
33
67
  </template>
@@ -0,0 +1,22 @@
1
+ import { createAuthClient } from "better-auth/vue";
2
+ {{#if (eq payments "polar")}}
3
+ import { polarClient } from "@polar-sh/better-auth";
4
+ {{/if}}
5
+
6
+ export default defineNuxtPlugin((nuxtApp) => {
7
+ const config = useRuntimeConfig();
8
+ const serverUrl = config.public.serverURL;
9
+
10
+ const authClient = createAuthClient({
11
+ baseURL: serverUrl,
12
+ {{#if (eq payments "polar")}}
13
+ plugins: [polarClient()],
14
+ {{/if}}
15
+ });
16
+
17
+ return {
18
+ provide: {
19
+ authClient: authClient,
20
+ },
21
+ };
22
+ });
@@ -1,4 +1,7 @@
1
1
  import { createAuthClient } from "better-auth/react";
2
+ {{#if (eq payments "polar")}}
3
+ import { polarClient } from "@polar-sh/better-auth";
4
+ {{/if}}
2
5
 
3
6
  export const authClient = createAuthClient({
4
7
  baseURL:
@@ -7,4 +10,7 @@ export const authClient = createAuthClient({
7
10
  {{else}}
8
11
  import.meta.env.VITE_SERVER_URL,
9
12
  {{/if}}
13
+ {{#if (eq payments "polar")}}
14
+ plugins: [polarClient()]
15
+ {{/if}}
10
16
  });
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { Button } from "@/components/ui/button";
3
+ import { authClient } from "@/lib/auth-client";
4
+ {{#if (eq api "orpc")}}
5
+ import { useQuery } from "@tanstack/react-query";
6
+ import { orpc } from "@/utils/orpc";
7
+ {{/if}}
8
+ {{#if (eq api "trpc")}}
9
+ import { useQuery } from "@tanstack/react-query";
10
+ import { trpc } from "@/utils/trpc";
11
+ {{/if}}
12
+
13
+ export default function Dashboard({
14
+ {{#if (eq payments "polar")}}
15
+ customerState,
16
+ {{/if}}
17
+ session
18
+ }: {
19
+ {{#if (eq payments "polar")}}
20
+ customerState: ReturnType<typeof authClient.customer.state>;
21
+ {{/if}}
22
+ session: typeof authClient.$Infer.Session;
23
+ }) {
24
+ {{#if (eq api "orpc")}}
25
+ const privateData = useQuery(orpc.privateData.queryOptions());
26
+ {{/if}}
27
+ {{#if (eq api "trpc")}}
28
+ const privateData = useQuery(trpc.privateData.queryOptions());
29
+ {{/if}}
30
+
31
+ {{#if (eq payments "polar")}}
32
+ const hasProSubscription = customerState?.activeSubscriptions?.length! > 0;
33
+ console.log("Active subscriptions:", customerState?.activeSubscriptions);
34
+ {{/if}}
35
+
36
+ return (
37
+ <>
38
+ {{#if (eq api "orpc")}}
39
+ <p>API: {privateData.data?.message}</p>
40
+ {{/if}}
41
+ {{#if (eq api "trpc")}}
42
+ <p>API: {privateData.data?.message}</p>
43
+ {{/if}}
44
+ {{#if (eq payments "polar")}}
45
+ <p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
46
+ {hasProSubscription ? (
47
+ <Button onClick={async () => await authClient.customer.portal()}>
48
+ Manage Subscription
49
+ </Button>
50
+ ) : (
51
+ <Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
52
+ Upgrade to Pro
53
+ </Button>
54
+ )}
55
+ {{/if}}
56
+ </>
57
+ );
58
+ }
@@ -1,47 +1,37 @@
1
- "use client"
2
1
  import { authClient } from "@/lib/auth-client";
3
- {{#if (eq api "orpc")}}
4
- import { useQuery } from "@tanstack/react-query";
5
- import { orpc } from "@/utils/orpc";
6
- {{/if}}
7
- {{#if (eq api "trpc")}}
8
- import { useQuery } from "@tanstack/react-query";
9
- import { trpc } from "@/utils/trpc";
10
- {{/if}}
11
- import { useRouter } from "next/navigation";
12
- import { useEffect } from "react";
2
+ import { redirect } from "next/navigation";
3
+ import Dashboard from "./dashboard";
4
+ import { headers } from "next/headers";
13
5
 
14
- export default function Dashboard() {
15
- const router = useRouter();
16
- const { data: session, isPending } = authClient.useSession();
6
+ export default async function DashboardPage() {
7
+ const session = await authClient.getSession({
8
+ fetchOptions: {
9
+ headers: await headers()
10
+ }
11
+ });
17
12
 
18
- {{#if (eq api "orpc")}}
19
- const privateData = useQuery(orpc.privateData.queryOptions());
20
- {{/if}}
21
- {{#if (eq api "trpc")}}
22
- const privateData = useQuery(trpc.privateData.queryOptions());
23
- {{/if}}
13
+ if (!session.data) {
14
+ redirect("/login");
15
+ }
24
16
 
25
- useEffect(() => {
26
- if (!session && !isPending) {
27
- router.push("/login");
28
- }
29
- }, [session, isPending]);
17
+ {{#if (eq payments "polar")}}
18
+ const { data: customerState, error } = await authClient.customer.state({
19
+ fetchOptions: {
20
+ headers: await headers()
21
+ }
22
+ });
23
+ {{/if}}
30
24
 
31
- if (isPending) {
32
- return <div>Loading...</div>;
33
- }
34
-
35
- return (
36
- <div>
37
- <h1>Dashboard</h1>
38
- <p>Welcome {session?.user.name}</p>
39
- {{#if (eq api "orpc")}}
40
- <p>privateData: {privateData.data?.message}</p>
41
- {{/if}}
42
- {{#if (eq api "trpc")}}
43
- <p>privateData: {privateData.data?.message}</p>
44
- {{/if}}
45
- </div>
46
- );
25
+ return (
26
+ <div>
27
+ <h1>Dashboard</h1>
28
+ <p>Welcome {session.data.user.name}</p>
29
+ <Dashboard
30
+ session={session.data}
31
+ {{#if (eq payments "polar")}}
32
+ customerState={customerState}
33
+ {{/if}}
34
+ />
35
+ </div>
36
+ );
47
37
  }
@@ -1,3 +1,4 @@
1
+ import { Button } from "@/components/ui/button";
1
2
  import { authClient } from "@/lib/auth-client";
2
3
  {{#if (eq api "orpc")}}
3
4
  import { orpc } from "@/utils/orpc";
@@ -8,12 +9,15 @@ import { trpc } from "@/utils/trpc";
8
9
  {{#if ( or (eq api "orpc") (eq api "trpc"))}}
9
10
  import { useQuery } from "@tanstack/react-query";
10
11
  {{/if}}
11
- import { useEffect } from "react";
12
+ import { useEffect, useState } from "react";
12
13
  import { useNavigate } from "react-router";
13
14
 
14
15
  export default function Dashboard() {
15
16
  const { data: session, isPending } = authClient.useSession();
16
17
  const navigate = useNavigate();
18
+ {{#if (eq payments "polar")}}
19
+ const [customerState, setCustomerState] = useState<any>(null);
20
+ {{/if}}
17
21
 
18
22
  {{#if (eq api "orpc")}}
19
23
  const privateData = useQuery(orpc.privateData.queryOptions());
@@ -26,18 +30,48 @@ export default function Dashboard() {
26
30
  if (!session && !isPending) {
27
31
  navigate("/login");
28
32
  }
29
- }, [session, isPending]);
33
+ }, [session, isPending, navigate]);
34
+
35
+ {{#if (eq payments "polar")}}
36
+ useEffect(() => {
37
+ async function fetchCustomerState() {
38
+ if (session) {
39
+ const { data } = await authClient.customer.state();
40
+ setCustomerState(data);
41
+ }
42
+ }
43
+
44
+ fetchCustomerState();
45
+ }, [session]);
46
+ {{/if}}
30
47
 
31
48
  if (isPending) {
32
49
  return <div>Loading...</div>;
33
50
  }
34
51
 
52
+ {{#if (eq payments "polar")}}
53
+ const hasProSubscription = customerState?.activeSubscriptions?.length! > 0;
54
+ console.log("Active subscriptions:", customerState?.activeSubscriptions);
55
+ {{/if}}
56
+
35
57
  return (
36
58
  <div>
37
59
  <h1>Dashboard</h1>
38
60
  <p>Welcome {session?.user.name}</p>
39
61
  {{#if ( or (eq api "orpc") (eq api "trpc"))}}
40
- <p>privateData: {privateData.data?.message}</p>
62
+ <p>API: {privateData.data?.message}</p>
63
+ {{/if}}
64
+ {{#if (eq payments "polar")}}
65
+ <p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
66
+ {hasProSubscription ? (
67
+ <Button onClick={async () => await authClient.customer.portal()}>
68
+ Manage Subscription
69
+ </Button>
70
+ ) : (
71
+ <Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
72
+ Upgrade to Pro
73
+ </Button>
74
+ )}
41
75
  {{/if}}
42
76
  </div>
43
77
  );
@@ -1,3 +1,4 @@
1
+ import { Button } from "@/components/ui/button";
1
2
  import { authClient } from "@/lib/auth-client";
2
3
  {{#if (eq api "orpc")}}
3
4
  import { orpc } from "@/utils/orpc";
@@ -8,44 +9,61 @@ import { trpc } from "@/utils/trpc";
8
9
  {{#if ( or (eq api "orpc") (eq api "trpc"))}}
9
10
  import { useQuery } from "@tanstack/react-query";
10
11
  {{/if}}
11
- import { createFileRoute } from "@tanstack/react-router";
12
- import { useEffect } from "react";
12
+ import { createFileRoute, redirect } from "@tanstack/react-router";
13
13
 
14
14
  export const Route = createFileRoute("/dashboard")({
15
- component: RouteComponent,
15
+ component: RouteComponent,
16
+ beforeLoad: async () => {
17
+ const session = await authClient.getSession();
18
+ if (!session.data) {
19
+ redirect({
20
+ to: "/login",
21
+ throw: true
22
+ });
23
+ }
24
+ {{#if (eq payments "polar")}}
25
+ const {data: customerState} = await authClient.customer.state()
26
+ return { session, customerState };
27
+ {{else}}
28
+ return { session };
29
+ {{/if}}
30
+ }
16
31
  });
17
32
 
18
33
  function RouteComponent() {
19
- const { data: session, isPending } = authClient.useSession();
34
+ const { session{{#if (eq payments "polar")}}, customerState{{/if}} } = Route.useRouteContext();
20
35
 
21
- const navigate = Route.useNavigate();
36
+ {{#if (eq api "orpc")}}
37
+ const privateData = useQuery(orpc.privateData.queryOptions());
38
+ {{/if}}
39
+ {{#if (eq api "trpc")}}
40
+ const privateData = useQuery(trpc.privateData.queryOptions());
41
+ {{/if}}
22
42
 
23
- {{#if (eq api "orpc")}}
24
- const privateData = useQuery(orpc.privateData.queryOptions());
25
- {{/if}}
26
- {{#if (eq api "trpc")}}
27
- const privateData = useQuery(trpc.privateData.queryOptions());
28
- {{/if}}
43
+ {{#if (eq payments "polar")}}
44
+ const hasProSubscription = customerState?.activeSubscriptions?.length! > 0
45
+ console.log("Active subscriptions:", customerState?.activeSubscriptions)
46
+ {{/if}}
29
47
 
30
- useEffect(() => {
31
- if (!session && !isPending) {
32
- navigate({
33
- to: "/login",
34
- });
35
- }
36
- }, [session, isPending]);
37
-
38
- if (isPending) {
39
- return <div>Loading...</div>;
40
- }
41
-
42
- return (
43
- <div>
44
- <h1>Dashboard</h1>
45
- <p>Welcome {session?.user.name}</p>
46
- {{#if ( or (eq api "orpc") (eq api "trpc"))}}
47
- <p>privateData: {privateData.data?.message}</p>
48
- {{/if}}
49
- </div>
50
- );
48
+ return (
49
+ <div>
50
+ <h1>Dashboard</h1>
51
+ <p>Welcome {session.data?.user.name}</p>
52
+ {{#if ( or (eq api "orpc") (eq api "trpc"))}}
53
+ <p>API: {privateData.data?.message}</p>
54
+ {{/if}}
55
+ {{#if (eq payments "polar")}}
56
+ <p>Plan: {hasProSubscription ? "Pro" : "Free"}</p>
57
+ {hasProSubscription ? (
58
+ <Button onClick={async () => await authClient.customer.portal()}>
59
+ Manage Subscription
60
+ </Button>
61
+ ) : (
62
+ <Button onClick={async () => await authClient.checkout({ slug: "pro" })}>
63
+ Upgrade to Pro
64
+ </Button>
65
+ )}
66
+ {{/if}}
67
+ </div>
68
+ );
51
69
  }