create-better-t-stack 2.37.0 → 2.38.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.
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.js +1 -1
- package/dist/{src-CGVrE3la.js → src-BR5HLmM2.js} +266 -104
- package/package.json +1 -1
- package/templates/addons/ruler/.ruler/bts.md.hbs +2 -2
- package/templates/api/orpc/native/utils/orpc.ts.hbs +2 -2
- package/templates/api/orpc/server/base/src/lib/context.ts.hbs +10 -10
- package/templates/api/orpc/server/base/src/lib/orpc.ts.hbs +1 -1
- package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +2 -2
- package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
- package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +11 -1
- package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
- package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
- package/templates/api/trpc/native/utils/trpc.ts.hbs +2 -2
- package/templates/api/trpc/server/base/src/lib/context.ts.hbs +10 -10
- package/templates/api/trpc/server/base/src/lib/trpc.ts.hbs +1 -1
- package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +2 -2
- package/templates/auth/clerk/convex/backend/convex/auth.config.ts.hbs +12 -0
- package/templates/auth/clerk/convex/backend/convex/privateData.ts.hbs +16 -0
- package/templates/auth/clerk/convex/native/base/app/(auth)/_layout.tsx.hbs +12 -0
- package/templates/auth/clerk/convex/native/base/app/(auth)/sign-in.tsx.hbs +67 -0
- package/templates/auth/clerk/convex/native/base/app/(auth)/sign-out.tsx.hbs +110 -0
- package/templates/auth/clerk/convex/native/base/components/sign-out-button.tsx.hbs +27 -0
- package/templates/auth/clerk/convex/web/react/next/src/app/dashboard/page.tsx.hbs +29 -0
- package/templates/auth/clerk/convex/web/react/next/src/middleware.ts.hbs +12 -0
- package/templates/auth/clerk/convex/web/react/react-router/src/routes/dashboard.tsx.hbs +32 -0
- package/templates/auth/clerk/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +37 -0
- package/templates/auth/clerk/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +37 -0
- package/templates/auth/clerk/convex/web/react/tanstack-start/src/server.ts.hbs +18 -0
- package/templates/backend/convex/packages/backend/package.json.hbs +1 -0
- package/templates/backend/server/elysia/src/index.ts.hbs +3 -3
- package/templates/backend/server/express/src/index.ts.hbs +6 -6
- package/templates/backend/server/fastify/src/index.ts.hbs +4 -4
- package/templates/backend/server/hono/src/index.ts.hbs +4 -4
- package/templates/backend/server/server-base/src/routers/index.ts.hbs +4 -4
- package/templates/deploy/alchemy/alchemy.run.ts.hbs +2 -2
- package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +35 -3
- package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +28 -0
- package/templates/frontend/native/nativewind/package.json.hbs +1 -0
- package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +32 -0
- package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +35 -0
- package/templates/frontend/native/unistyles/package.json.hbs +1 -0
- package/templates/frontend/nuxt/app/components/Header.vue.hbs +3 -3
- package/templates/frontend/react/next/src/app/layout.tsx.hbs +23 -15
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +12 -0
- package/templates/frontend/react/react-router/src/root.tsx.hbs +28 -1
- package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +19 -1
- package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +8 -4
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +52 -5
- package/templates/frontend/react/web-base/src/components/header.tsx.hbs +3 -3
- package/templates/frontend/solid/src/components/header.tsx.hbs +3 -3
- package/templates/frontend/svelte/src/components/Header.svelte.hbs +3 -3
- /package/templates/auth/{native → better-auth/native}/native-base/lib/auth-client.ts.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/nativewind/app/(drawer)/index.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-in.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-up.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/unistyles/app/(drawer)/index.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-in.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-up.tsx.hbs +0 -0
- /package/templates/auth/{server → better-auth/server}/base/src/lib/auth.ts.hbs +0 -0
- /package/templates/auth/{server → better-auth/server}/db/drizzle/mysql/src/db/schema/auth.ts +0 -0
- /package/templates/auth/{server → better-auth/server}/db/drizzle/postgres/src/db/schema/auth.ts +0 -0
- /package/templates/auth/{server → better-auth/server}/db/drizzle/sqlite/src/db/schema/auth.ts +0 -0
- /package/templates/auth/{server → better-auth/server}/db/mongoose/mongodb/src/db/models/auth.model.ts +0 -0
- /package/templates/auth/{server → better-auth/server}/db/prisma/mongodb/prisma/schema/auth.prisma +0 -0
- /package/templates/auth/{server → better-auth/server}/db/prisma/mysql/prisma/schema/auth.prisma +0 -0
- /package/templates/auth/{server → better-auth/server}/db/prisma/postgres/prisma/schema/auth.prisma +0 -0
- /package/templates/auth/{server → better-auth/server}/db/prisma/sqlite/prisma/schema/auth.prisma +0 -0
- /package/templates/auth/{server → better-auth/server}/next/src/app/api/auth/[...all]/route.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignInForm.vue +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignUpForm.vue +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/components/UserMenu.vue +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/middleware/auth.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/dashboard.vue.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/login.vue +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/plugins/auth-client.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/react/base/src/lib/auth-client.ts.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/app/dashboard/page.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/app/login/page.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/components/theme-provider.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/components/user-menu.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/user-menu.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/dashboard.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/login.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/user-menu.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/dashboard.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/login.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/user-menu.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/dashboard.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/login.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/components/user-menu.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/lib/auth-client.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/routes/dashboard.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/routes/login.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignInForm.svelte +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignUpForm.svelte +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/components/UserMenu.svelte +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/lib/auth-client.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/routes/dashboard/+page.svelte.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/routes/login/+page.svelte +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.38.0",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -77,7 +77,7 @@ Database models are located in `apps/server/src/db/models/`
|
|
|
77
77
|
{{/if}}
|
|
78
78
|
{{/if}}
|
|
79
79
|
|
|
80
|
-
{{#if auth}}
|
|
80
|
+
{{#if (eq auth "better-auth")}}
|
|
81
81
|
## Authentication
|
|
82
82
|
|
|
83
83
|
Authentication is enabled in this project:
|
|
@@ -129,4 +129,4 @@ This project includes a `bts.jsonc` configuration file that stores your Better-T
|
|
|
129
129
|
- Turborepo handles build caching and parallel execution
|
|
130
130
|
{{/if}}
|
|
131
131
|
- Use `{{#if (eq packageManager "bun")}}bunx{{else if (eq packageManager "pnpm")}}pnpx{{else}}npx{{/if}}
|
|
132
|
-
create-better-t-stack add` to add more features later
|
|
132
|
+
create-better-t-stack add` to add more features later
|
|
@@ -3,7 +3,7 @@ import { RPCLink } from "@orpc/client/fetch";
|
|
|
3
3
|
import { createTanstackQueryUtils } from "@orpc/tanstack-query";
|
|
4
4
|
import { QueryCache, QueryClient } from "@tanstack/react-query";
|
|
5
5
|
import type { AppRouterClient } from "../../server/src/routers";
|
|
6
|
-
{{#if auth}}
|
|
6
|
+
{{#if (eq auth "better-auth")}}
|
|
7
7
|
import { authClient } from "@/lib/auth-client";
|
|
8
8
|
{{/if}}
|
|
9
9
|
|
|
@@ -17,7 +17,7 @@ export const queryClient = new QueryClient({
|
|
|
17
17
|
|
|
18
18
|
export const link = new RPCLink({
|
|
19
19
|
url: `${process.env.EXPO_PUBLIC_SERVER_URL}/rpc`,
|
|
20
|
-
{{#if auth}}
|
|
20
|
+
{{#if (eq auth "better-auth")}}
|
|
21
21
|
headers() {
|
|
22
22
|
const headers = new Map<string, string>();
|
|
23
23
|
const cookies = authClient.getCookie();
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{{#if (eq backend 'next')}}
|
|
2
2
|
import type { NextRequest } from "next/server";
|
|
3
|
-
{{#if auth}}
|
|
3
|
+
{{#if (eq auth "better-auth")}}
|
|
4
4
|
import { auth } from "./auth";
|
|
5
5
|
{{/if}}
|
|
6
6
|
|
|
7
7
|
export async function createContext(req: NextRequest) {
|
|
8
|
-
{{#if auth}}
|
|
8
|
+
{{#if (eq auth "better-auth")}}
|
|
9
9
|
const session = await auth.api.getSession({
|
|
10
10
|
headers: req.headers,
|
|
11
11
|
});
|
|
@@ -19,7 +19,7 @@ export async function createContext(req: NextRequest) {
|
|
|
19
19
|
|
|
20
20
|
{{else if (eq backend 'hono')}}
|
|
21
21
|
import type { Context as HonoContext } from "hono";
|
|
22
|
-
{{#if auth}}
|
|
22
|
+
{{#if (eq auth "better-auth")}}
|
|
23
23
|
import { auth } from "./auth";
|
|
24
24
|
{{/if}}
|
|
25
25
|
|
|
@@ -28,7 +28,7 @@ export type CreateContextOptions = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
export async function createContext({ context }: CreateContextOptions) {
|
|
31
|
-
{{#if auth}}
|
|
31
|
+
{{#if (eq auth "better-auth")}}
|
|
32
32
|
const session = await auth.api.getSession({
|
|
33
33
|
headers: context.req.raw.headers,
|
|
34
34
|
});
|
|
@@ -45,7 +45,7 @@ export async function createContext({ context }: CreateContextOptions) {
|
|
|
45
45
|
|
|
46
46
|
{{else if (eq backend 'elysia')}}
|
|
47
47
|
import type { Context as ElysiaContext } from "elysia";
|
|
48
|
-
{{#if auth}}
|
|
48
|
+
{{#if (eq auth "better-auth")}}
|
|
49
49
|
import { auth } from "./auth";
|
|
50
50
|
{{/if}}
|
|
51
51
|
|
|
@@ -54,7 +54,7 @@ export type CreateContextOptions = {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
export async function createContext({ context }: CreateContextOptions) {
|
|
57
|
-
{{#if auth}}
|
|
57
|
+
{{#if (eq auth "better-auth")}}
|
|
58
58
|
const session = await auth.api.getSession({
|
|
59
59
|
headers: context.request.headers,
|
|
60
60
|
});
|
|
@@ -70,13 +70,13 @@ export async function createContext({ context }: CreateContextOptions) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
{{else if (eq backend 'express')}}
|
|
73
|
-
{{#if auth}}
|
|
73
|
+
{{#if (eq auth "better-auth")}}
|
|
74
74
|
import { fromNodeHeaders } from "better-auth/node";
|
|
75
75
|
import { auth } from "./auth";
|
|
76
76
|
{{/if}}
|
|
77
77
|
|
|
78
78
|
export async function createContext(opts: any) {
|
|
79
|
-
{{#if auth}}
|
|
79
|
+
{{#if (eq auth "better-auth")}}
|
|
80
80
|
const session = await auth.api.getSession({
|
|
81
81
|
headers: fromNodeHeaders(opts.req.headers),
|
|
82
82
|
});
|
|
@@ -93,13 +93,13 @@ export async function createContext(opts: any) {
|
|
|
93
93
|
|
|
94
94
|
{{else if (eq backend 'fastify')}}
|
|
95
95
|
import type { IncomingHttpHeaders } from "node:http";
|
|
96
|
-
{{#if auth}}
|
|
96
|
+
{{#if (eq auth "better-auth")}}
|
|
97
97
|
import { fromNodeHeaders } from "better-auth/node";
|
|
98
98
|
import { auth } from "./auth";
|
|
99
99
|
{{/if}}
|
|
100
100
|
|
|
101
101
|
export async function createContext(req: IncomingHttpHeaders) {
|
|
102
|
-
{{#if auth}}
|
|
102
|
+
{{#if (eq auth "better-auth")}}
|
|
103
103
|
const session = await auth.api.getSession({
|
|
104
104
|
headers: fromNodeHeaders(req),
|
|
105
105
|
});
|
|
@@ -5,7 +5,7 @@ export const o = os.$context<Context>();
|
|
|
5
5
|
|
|
6
6
|
export const publicProcedure = o;
|
|
7
7
|
|
|
8
|
-
{{#if auth}}
|
|
8
|
+
{{#if (eq auth "better-auth")}}
|
|
9
9
|
const requireAuth = o.middleware(async ({ context, next }) => {
|
|
10
10
|
if (!context.session?.user) {
|
|
11
11
|
throw new ORPCError("UNAUTHORIZED");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{{#if auth}}
|
|
1
|
+
{{#if (eq auth "better-auth")}}
|
|
2
2
|
import { createContext } from '@/lib/context'
|
|
3
3
|
{{/if}}
|
|
4
4
|
import { appRouter } from '@/routers'
|
|
@@ -10,7 +10,7 @@ const handler = new RPCHandler(appRouter)
|
|
|
10
10
|
async function handleRequest(req: NextRequest) {
|
|
11
11
|
const { response } = await handler.handle(req, {
|
|
12
12
|
prefix: '/rpc',
|
|
13
|
-
context: {{#if auth}}await createContext(req){{else}}{}{{/if}},
|
|
13
|
+
context: {{#if (eq auth "better-auth")}}await createContext(req){{else}}{}{{/if}},
|
|
14
14
|
})
|
|
15
15
|
|
|
16
16
|
return response ?? new Response('Not found', { status: 404 })
|
|
@@ -26,13 +26,23 @@ export const link = new RPCLink({
|
|
|
26
26
|
{{else}}
|
|
27
27
|
url: `${import.meta.env.VITE_SERVER_URL}/rpc`,
|
|
28
28
|
{{/if}}
|
|
29
|
-
{{#if auth}}
|
|
29
|
+
{{#if (eq auth "better-auth")}}
|
|
30
30
|
fetch(url, options) {
|
|
31
31
|
return fetch(url, {
|
|
32
32
|
...options,
|
|
33
33
|
credentials: "include",
|
|
34
34
|
});
|
|
35
35
|
},
|
|
36
|
+
{{#if (includes frontend "next")}}
|
|
37
|
+
headers: async () => {
|
|
38
|
+
if (typeof window !== "undefined") {
|
|
39
|
+
return {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { headers } = await import("next/headers")
|
|
43
|
+
return Object.fromEntries(await headers())
|
|
44
|
+
},
|
|
45
|
+
{{/if}}
|
|
36
46
|
{{/if}}
|
|
37
47
|
});
|
|
38
48
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{{#if auth}}
|
|
1
|
+
{{#if (eq auth "better-auth")}}
|
|
2
2
|
import { authClient } from "@/lib/auth-client";
|
|
3
3
|
{{/if}}
|
|
4
4
|
import { QueryClient } from "@tanstack/react-query";
|
|
@@ -12,7 +12,7 @@ const trpcClient = createTRPCClient<AppRouter>({
|
|
|
12
12
|
links: [
|
|
13
13
|
httpBatchLink({
|
|
14
14
|
url: `${process.env.EXPO_PUBLIC_SERVER_URL}/trpc`,
|
|
15
|
-
{{#if auth}}
|
|
15
|
+
{{#if (eq auth "better-auth")}}
|
|
16
16
|
headers() {
|
|
17
17
|
const headers = new Map<string, string>();
|
|
18
18
|
const cookies = authClient.getCookie();
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{{#if (eq backend 'next')}}
|
|
2
2
|
import type { NextRequest } from "next/server";
|
|
3
|
-
{{#if auth}}
|
|
3
|
+
{{#if (eq auth "better-auth")}}
|
|
4
4
|
import { auth } from "./auth";
|
|
5
5
|
{{/if}}
|
|
6
6
|
|
|
7
7
|
export async function createContext(req: NextRequest) {
|
|
8
|
-
{{#if auth}}
|
|
8
|
+
{{#if (eq auth "better-auth")}}
|
|
9
9
|
const session = await auth.api.getSession({
|
|
10
10
|
headers: req.headers,
|
|
11
11
|
});
|
|
@@ -22,7 +22,7 @@ export async function createContext(req: NextRequest) {
|
|
|
22
22
|
|
|
23
23
|
{{else if (eq backend 'hono')}}
|
|
24
24
|
import type { Context as HonoContext } from "hono";
|
|
25
|
-
{{#if auth}}
|
|
25
|
+
{{#if (eq auth "better-auth")}}
|
|
26
26
|
import { auth } from "./auth";
|
|
27
27
|
{{/if}}
|
|
28
28
|
|
|
@@ -31,7 +31,7 @@ export type CreateContextOptions = {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
export async function createContext({ context }: CreateContextOptions) {
|
|
34
|
-
{{#if auth}}
|
|
34
|
+
{{#if (eq auth "better-auth")}}
|
|
35
35
|
const session = await auth.api.getSession({
|
|
36
36
|
headers: context.req.raw.headers,
|
|
37
37
|
});
|
|
@@ -48,7 +48,7 @@ export async function createContext({ context }: CreateContextOptions) {
|
|
|
48
48
|
|
|
49
49
|
{{else if (eq backend 'elysia')}}
|
|
50
50
|
import type { Context as ElysiaContext } from "elysia";
|
|
51
|
-
{{#if auth}}
|
|
51
|
+
{{#if (eq auth "better-auth")}}
|
|
52
52
|
import { auth } from "./auth";
|
|
53
53
|
{{/if}}
|
|
54
54
|
|
|
@@ -57,7 +57,7 @@ export type CreateContextOptions = {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
export async function createContext({ context }: CreateContextOptions) {
|
|
60
|
-
{{#if auth}}
|
|
60
|
+
{{#if (eq auth "better-auth")}}
|
|
61
61
|
const session = await auth.api.getSession({
|
|
62
62
|
headers: context.request.headers,
|
|
63
63
|
});
|
|
@@ -74,13 +74,13 @@ export async function createContext({ context }: CreateContextOptions) {
|
|
|
74
74
|
|
|
75
75
|
{{else if (eq backend 'express')}}
|
|
76
76
|
import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
|
|
77
|
-
{{#if auth}}
|
|
77
|
+
{{#if (eq auth "better-auth")}}
|
|
78
78
|
import { fromNodeHeaders } from "better-auth/node";
|
|
79
79
|
import { auth } from "./auth";
|
|
80
80
|
{{/if}}
|
|
81
81
|
|
|
82
82
|
export async function createContext(opts: CreateExpressContextOptions) {
|
|
83
|
-
{{#if auth}}
|
|
83
|
+
{{#if (eq auth "better-auth")}}
|
|
84
84
|
const session = await auth.api.getSession({
|
|
85
85
|
headers: fromNodeHeaders(opts.req.headers),
|
|
86
86
|
});
|
|
@@ -97,13 +97,13 @@ export async function createContext(opts: CreateExpressContextOptions) {
|
|
|
97
97
|
|
|
98
98
|
{{else if (eq backend 'fastify')}}
|
|
99
99
|
import type { CreateFastifyContextOptions } from "@trpc/server/adapters/fastify";
|
|
100
|
-
{{#if auth}}
|
|
100
|
+
{{#if (eq auth "better-auth")}}
|
|
101
101
|
import { fromNodeHeaders } from "better-auth/node";
|
|
102
102
|
import { auth } from "./auth";
|
|
103
103
|
{{/if}}
|
|
104
104
|
|
|
105
105
|
export async function createContext({ req, res }: CreateFastifyContextOptions) {
|
|
106
|
-
{{#if auth}}
|
|
106
|
+
{{#if (eq auth "better-auth")}}
|
|
107
107
|
const session = await auth.api.getSession({
|
|
108
108
|
headers: fromNodeHeaders(req.headers),
|
|
109
109
|
});
|
|
@@ -28,7 +28,7 @@ const trpcClient = createTRPCClient<AppRouter>({
|
|
|
28
28
|
{{else}}
|
|
29
29
|
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
|
|
30
30
|
{{/if}}
|
|
31
|
-
{{#if auth}}
|
|
31
|
+
{{#if (eq auth "better-auth")}}
|
|
32
32
|
fetch(url, options) {
|
|
33
33
|
return fetch(url, {
|
|
34
34
|
...options,
|
|
@@ -78,7 +78,7 @@ export const trpcClient = createTRPCClient<AppRouter>({
|
|
|
78
78
|
links: [
|
|
79
79
|
httpBatchLink({
|
|
80
80
|
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
|
|
81
|
-
{{#if auth}}
|
|
81
|
+
{{#if (eq auth "better-auth")}}
|
|
82
82
|
fetch(url, options) {
|
|
83
83
|
return fetch(url, {
|
|
84
84
|
...options,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
providers: [
|
|
3
|
+
{
|
|
4
|
+
// Replace with your own Clerk Issuer URL from your "convex" JWT template
|
|
5
|
+
// or with `process.env.CLERK_JWT_ISSUER_DOMAIN`
|
|
6
|
+
// and configure CLERK_JWT_ISSUER_DOMAIN on the Convex Dashboard
|
|
7
|
+
// See https://docs.convex.dev/auth/clerk#configuring-dev-and-prod-instances
|
|
8
|
+
domain: process.env.CLERK_JWT_ISSUER_DOMAIN,
|
|
9
|
+
applicationID: "convex",
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { query } from "./_generated/server";
|
|
2
|
+
|
|
3
|
+
export const get = query({
|
|
4
|
+
args: {},
|
|
5
|
+
handler: async (ctx) => {
|
|
6
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
7
|
+
if (identity === null) {
|
|
8
|
+
return {
|
|
9
|
+
message: "Not authenticated",
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
message: "This is private",
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Redirect, Stack } from "expo-router";
|
|
2
|
+
import { useAuth } from "@clerk/clerk-expo";
|
|
3
|
+
|
|
4
|
+
export default function AuthRoutesLayout() {
|
|
5
|
+
const { isSignedIn } = useAuth();
|
|
6
|
+
|
|
7
|
+
if (isSignedIn) {
|
|
8
|
+
return <Redirect href={"/"} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return <Stack />;
|
|
12
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useSignIn } from "@clerk/clerk-expo";
|
|
2
|
+
import { Link, useRouter } from "expo-router";
|
|
3
|
+
import { Text, TextInput, TouchableOpacity, View } from "react-native";
|
|
4
|
+
import React from "react";
|
|
5
|
+
|
|
6
|
+
export default function Page() {
|
|
7
|
+
const { signIn, setActive, isLoaded } = useSignIn();
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
|
|
10
|
+
const [emailAddress, setEmailAddress] = React.useState("");
|
|
11
|
+
const [password, setPassword] = React.useState("");
|
|
12
|
+
|
|
13
|
+
// Handle the submission of the sign-in form
|
|
14
|
+
const onSignInPress = async () => {
|
|
15
|
+
if (!isLoaded) return;
|
|
16
|
+
|
|
17
|
+
// Start the sign-in process using the email and password provided
|
|
18
|
+
try {
|
|
19
|
+
const signInAttempt = await signIn.create({
|
|
20
|
+
identifier: emailAddress,
|
|
21
|
+
password,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// If sign-in process is complete, set the created session as active
|
|
25
|
+
// and redirect the user
|
|
26
|
+
if (signInAttempt.status === "complete") {
|
|
27
|
+
await setActive({ session: signInAttempt.createdSessionId });
|
|
28
|
+
router.replace("/");
|
|
29
|
+
} else {
|
|
30
|
+
// If the status isn't complete, check why. User might need to
|
|
31
|
+
// complete further steps.
|
|
32
|
+
console.error(JSON.stringify(signInAttempt, null, 2));
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
// See https://clerk.com/docs/custom-flows/error-handling
|
|
36
|
+
// for more info on error handling
|
|
37
|
+
console.error(JSON.stringify(err, null, 2));
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<View>
|
|
43
|
+
<Text>Sign in</Text>
|
|
44
|
+
<TextInput
|
|
45
|
+
autoCapitalize="none"
|
|
46
|
+
value={emailAddress}
|
|
47
|
+
placeholder="Enter email"
|
|
48
|
+
onChangeText={(emailAddress) => setEmailAddress(emailAddress)}
|
|
49
|
+
/>
|
|
50
|
+
<TextInput
|
|
51
|
+
value={password}
|
|
52
|
+
placeholder="Enter password"
|
|
53
|
+
secureTextEntry={true}
|
|
54
|
+
onChangeText={(password) => setPassword(password)}
|
|
55
|
+
/>
|
|
56
|
+
<TouchableOpacity onPress={onSignInPress}>
|
|
57
|
+
<Text>Continue</Text>
|
|
58
|
+
</TouchableOpacity>
|
|
59
|
+
<View style=\{{ display: "flex", flexDirection: "row", gap: 3 }}>
|
|
60
|
+
<Text>Don't have an account?</Text>
|
|
61
|
+
<Link href="/sign-up">
|
|
62
|
+
<Text>Sign up</Text>
|
|
63
|
+
</Link>
|
|
64
|
+
</View>
|
|
65
|
+
</View>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Text, TextInput, TouchableOpacity, View } from "react-native";
|
|
3
|
+
import { useSignUp } from "@clerk/clerk-expo";
|
|
4
|
+
import { Link, useRouter } from "expo-router";
|
|
5
|
+
|
|
6
|
+
export default function SignUpScreen() {
|
|
7
|
+
const { isLoaded, signUp, setActive } = useSignUp();
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
|
|
10
|
+
const [emailAddress, setEmailAddress] = React.useState("");
|
|
11
|
+
const [password, setPassword] = React.useState("");
|
|
12
|
+
const [pendingVerification, setPendingVerification] = React.useState(false);
|
|
13
|
+
const [code, setCode] = React.useState("");
|
|
14
|
+
|
|
15
|
+
// Handle submission of sign-up form
|
|
16
|
+
const onSignUpPress = async () => {
|
|
17
|
+
if (!isLoaded) return;
|
|
18
|
+
|
|
19
|
+
console.log(emailAddress, password);
|
|
20
|
+
|
|
21
|
+
// Start sign-up process using email and password provided
|
|
22
|
+
try {
|
|
23
|
+
await signUp.create({
|
|
24
|
+
emailAddress,
|
|
25
|
+
password,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Send user an email with verification code
|
|
29
|
+
await signUp.prepareEmailAddressVerification({ strategy: "email_code" });
|
|
30
|
+
|
|
31
|
+
// Set 'pendingVerification' to true to display second form
|
|
32
|
+
// and capture OTP code
|
|
33
|
+
setPendingVerification(true);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
// See https://clerk.com/docs/custom-flows/error-handling
|
|
36
|
+
// for more info on error handling
|
|
37
|
+
console.error(JSON.stringify(err, null, 2));
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Handle submission of verification form
|
|
42
|
+
const onVerifyPress = async () => {
|
|
43
|
+
if (!isLoaded) return;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// Use the code the user provided to attempt verification
|
|
47
|
+
const signUpAttempt = await signUp.attemptEmailAddressVerification({
|
|
48
|
+
code,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// If verification was completed, set the session to active
|
|
52
|
+
// and redirect the user
|
|
53
|
+
if (signUpAttempt.status === "complete") {
|
|
54
|
+
await setActive({ session: signUpAttempt.createdSessionId });
|
|
55
|
+
router.replace("/");
|
|
56
|
+
} else {
|
|
57
|
+
// If the status is not complete, check why. User may need to
|
|
58
|
+
// complete further steps.
|
|
59
|
+
console.error(JSON.stringify(signUpAttempt, null, 2));
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
// See https://clerk.com/docs/custom-flows/error-handling
|
|
63
|
+
// for more info on error handling
|
|
64
|
+
console.error(JSON.stringify(err, null, 2));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (pendingVerification) {
|
|
69
|
+
return (
|
|
70
|
+
<>
|
|
71
|
+
<Text>Verify your email</Text>
|
|
72
|
+
<TextInput
|
|
73
|
+
value={code}
|
|
74
|
+
placeholder="Enter your verification code"
|
|
75
|
+
onChangeText={(code) => setCode(code)}
|
|
76
|
+
/>
|
|
77
|
+
<TouchableOpacity onPress={onVerifyPress}>
|
|
78
|
+
<Text>Verify</Text>
|
|
79
|
+
</TouchableOpacity>
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<View>
|
|
86
|
+
<Text>Sign up</Text>
|
|
87
|
+
<TextInput
|
|
88
|
+
autoCapitalize="none"
|
|
89
|
+
value={emailAddress}
|
|
90
|
+
placeholder="Enter email"
|
|
91
|
+
onChangeText={(email) => setEmailAddress(email)}
|
|
92
|
+
/>
|
|
93
|
+
<TextInput
|
|
94
|
+
value={password}
|
|
95
|
+
placeholder="Enter password"
|
|
96
|
+
secureTextEntry={true}
|
|
97
|
+
onChangeText={(password) => setPassword(password)}
|
|
98
|
+
/>
|
|
99
|
+
<TouchableOpacity onPress={onSignUpPress}>
|
|
100
|
+
<Text>Continue</Text>
|
|
101
|
+
</TouchableOpacity>
|
|
102
|
+
<View style=\{{ display: "flex", flexDirection: "row", gap: 3 }}>
|
|
103
|
+
<Text>Already have an account?</Text>
|
|
104
|
+
<Link href="/sign-in">
|
|
105
|
+
<Text>Sign in</Text>
|
|
106
|
+
</Link>
|
|
107
|
+
</View>
|
|
108
|
+
</View>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useClerk } from "@clerk/clerk-expo";
|
|
2
|
+
import { useRouter } from "expo-router";
|
|
3
|
+
import { Text, TouchableOpacity } from "react-native";
|
|
4
|
+
|
|
5
|
+
export const SignOutButton = () => {
|
|
6
|
+
// Use `useClerk()` to access the `signOut()` function
|
|
7
|
+
const { signOut } = useClerk();
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
|
|
10
|
+
const handleSignOut = async () => {
|
|
11
|
+
try {
|
|
12
|
+
await signOut();
|
|
13
|
+
// Redirect to your desired page
|
|
14
|
+
router.replace("/");
|
|
15
|
+
} catch (err) {
|
|
16
|
+
// See https://clerk.com/docs/custom-flows/error-handling
|
|
17
|
+
// for more info on error handling
|
|
18
|
+
console.error(JSON.stringify(err, null, 2));
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<TouchableOpacity onPress={handleSignOut}>
|
|
24
|
+
<Text>Sign out</Text>
|
|
25
|
+
</TouchableOpacity>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
4
|
+
import { SignInButton, UserButton, useUser } from "@clerk/nextjs";
|
|
5
|
+
import { Authenticated, AuthLoading, Unauthenticated, useQuery } from "convex/react";
|
|
6
|
+
|
|
7
|
+
export default function Dashboard() {
|
|
8
|
+
const user = useUser();
|
|
9
|
+
const privateData = useQuery(api.privateData.get);
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<Authenticated>
|
|
14
|
+
<div>
|
|
15
|
+
<h1>Dashboard</h1>
|
|
16
|
+
<p>Welcome {user.user?.fullName}</p>
|
|
17
|
+
<p>privateData: {privateData?.message}</p>
|
|
18
|
+
<UserButton />
|
|
19
|
+
</div>
|
|
20
|
+
</Authenticated>
|
|
21
|
+
<Unauthenticated>
|
|
22
|
+
<SignInButton />
|
|
23
|
+
</Unauthenticated>
|
|
24
|
+
<AuthLoading>
|
|
25
|
+
<div>Loading...</div>
|
|
26
|
+
</AuthLoading>
|
|
27
|
+
</>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { clerkMiddleware } from "@clerk/nextjs/server";
|
|
2
|
+
|
|
3
|
+
export default clerkMiddleware();
|
|
4
|
+
|
|
5
|
+
export const config = {
|
|
6
|
+
matcher: [
|
|
7
|
+
// Skip Next.js internals and all static files, unless found in search params
|
|
8
|
+
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
|
|
9
|
+
// Always run for API routes
|
|
10
|
+
"/(api|trpc)(.*)",
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SignInButton, UserButton, useUser } from "@clerk/clerk-react";
|
|
2
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
3
|
+
import {
|
|
4
|
+
Authenticated,
|
|
5
|
+
AuthLoading,
|
|
6
|
+
Unauthenticated,
|
|
7
|
+
useQuery,
|
|
8
|
+
} from "convex/react";
|
|
9
|
+
|
|
10
|
+
export default function Dashboard() {
|
|
11
|
+
const privateData = useQuery(api.privateData.get);
|
|
12
|
+
const user = useUser();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
<Authenticated>
|
|
17
|
+
<div>
|
|
18
|
+
<h1>Dashboard</h1>
|
|
19
|
+
<p>Welcome {user.user?.fullName}</p>
|
|
20
|
+
<p>privateData: {privateData?.message}</p>
|
|
21
|
+
<UserButton />
|
|
22
|
+
</div>
|
|
23
|
+
</Authenticated>
|
|
24
|
+
<Unauthenticated>
|
|
25
|
+
<SignInButton />
|
|
26
|
+
</Unauthenticated>
|
|
27
|
+
<AuthLoading>
|
|
28
|
+
<div>Loading...</div>
|
|
29
|
+
</AuthLoading>
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
}
|