create-better-t-stack 3.9.0-pr730.0ee9844 → 3.10.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.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-DW15ZRe9.mjs → src-QkFdHtZE.mjs} +14 -10
- package/package.json +2 -2
- package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +4 -6
- package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +1 -5
- package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +6 -11
- package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +14 -17
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +20 -21
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +16 -18
- package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +10 -6
- package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +21 -22
- package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +20 -22
- package/templates/auth/better-auth/web/react/tanstack-router/src/components/user-menu.tsx.hbs +22 -24
- package/templates/auth/better-auth/web/react/tanstack-start/src/components/user-menu.tsx.hbs +22 -24
- package/templates/backend/convex/packages/backend/convex/README.md +4 -4
- package/templates/backend/convex/packages/backend/convex/tsconfig.json.hbs +1 -1
- package/templates/frontend/react/next/src/components/mode-toggle.tsx.hbs +4 -6
- package/templates/frontend/react/react-router/src/components/mode-toggle.tsx.hbs +4 -6
- package/templates/frontend/react/tanstack-router/src/components/mode-toggle.tsx.hbs +4 -6
package/dist/cli.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-
|
|
2
|
+
import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-QkFdHtZE.mjs";
|
|
3
3
|
|
|
4
4
|
export { builder, createBtsCli, docs, init, router, sponsors };
|
|
@@ -1234,7 +1234,7 @@ const getLatestCLIVersion = () => {
|
|
|
1234
1234
|
*/
|
|
1235
1235
|
function isTelemetryEnabled() {
|
|
1236
1236
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1237
|
-
const BTS_TELEMETRY = "
|
|
1237
|
+
const BTS_TELEMETRY = "1";
|
|
1238
1238
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1239
1239
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1240
1240
|
return true;
|
|
@@ -1242,7 +1242,16 @@ function isTelemetryEnabled() {
|
|
|
1242
1242
|
|
|
1243
1243
|
//#endregion
|
|
1244
1244
|
//#region src/utils/analytics.ts
|
|
1245
|
-
|
|
1245
|
+
const CONVEX_INGEST_URL = "https://striped-seahorse-863.convex.site/api/analytics/ingest";
|
|
1246
|
+
async function sendConvexEvent(payload) {
|
|
1247
|
+
try {
|
|
1248
|
+
await fetch(CONVEX_INGEST_URL, {
|
|
1249
|
+
method: "POST",
|
|
1250
|
+
headers: { "Content-Type": "application/json" },
|
|
1251
|
+
body: JSON.stringify(payload)
|
|
1252
|
+
});
|
|
1253
|
+
} catch {}
|
|
1254
|
+
}
|
|
1246
1255
|
async function trackProjectCreation(config, disableAnalytics = false) {
|
|
1247
1256
|
if (!isTelemetryEnabled() || disableAnalytics) return;
|
|
1248
1257
|
const { projectName: _projectName, projectDir: _projectDir, relativePath: _relativePath, ...safeConfig } = config;
|
|
@@ -4479,7 +4488,6 @@ async function setupEnvironmentVariables(config) {
|
|
|
4479
4488
|
if (!await fs.pathExists(envLocalPath) || !(await fs.readFile(envLocalPath, "utf8")).includes("npx convex env set")) {
|
|
4480
4489
|
let siteUrlComments = "";
|
|
4481
4490
|
if (hasWeb) siteUrlComments += "# npx convex env set SITE_URL http://localhost:3001\n";
|
|
4482
|
-
if (hasNative) siteUrlComments += "# npx convex env set NATIVE_SITE_URL http://localhost:8081 # For Expo Web\n";
|
|
4483
4491
|
const convexCommands = `# Set Convex environment variables
|
|
4484
4492
|
# npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
|
|
4485
4493
|
${siteUrlComments}`;
|
|
@@ -4491,11 +4499,6 @@ ${siteUrlComments}`;
|
|
|
4491
4499
|
value: "",
|
|
4492
4500
|
condition: true,
|
|
4493
4501
|
comment: "Same as CONVEX_URL but ends in .site"
|
|
4494
|
-
}, {
|
|
4495
|
-
key: "NATIVE_SITE_URL",
|
|
4496
|
-
value: "http://localhost:8081",
|
|
4497
|
-
condition: true,
|
|
4498
|
-
comment: "Expo Web URL for authentication"
|
|
4499
4502
|
});
|
|
4500
4503
|
if (hasWeb) convexBackendVars.push({
|
|
4501
4504
|
key: hasNextJs ? "NEXT_PUBLIC_CONVEX_SITE_URL" : "VITE_CONVEX_SITE_URL",
|
|
@@ -6147,7 +6150,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6147
6150
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
6148
6151
|
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
6149
6152
|
const lintingInstructions = hasLinting ? getLintingInstructions(runCmd) : "";
|
|
6150
|
-
const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || []) : "";
|
|
6153
|
+
const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || [], runCmd) : "";
|
|
6151
6154
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
6152
6155
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
6153
6156
|
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
@@ -6213,13 +6216,14 @@ async function displayPostInstallInstructions(config) {
|
|
|
6213
6216
|
output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
|
|
6214
6217
|
consola$1.box(output);
|
|
6215
6218
|
}
|
|
6216
|
-
function getNativeInstructions(isConvex, isBackendSelf,
|
|
6219
|
+
function getNativeInstructions(isConvex, isBackendSelf, frontend, runCmd) {
|
|
6217
6220
|
const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
|
|
6218
6221
|
const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : isBackendSelf ? "http://<YOUR_LOCAL_IP>:3001" : "http://<YOUR_LOCAL_IP>:3000";
|
|
6219
6222
|
const envFileName = ".env";
|
|
6220
6223
|
const ipNote = isConvex ? "your Convex deployment URL (find after running 'dev:setup')" : "your local IP address";
|
|
6221
6224
|
let instructions = `${pc.yellow("NOTE:")} For Expo connectivity issues, update\n apps/native/${envFileName} with ${ipNote}:\n ${`${envVar}=${exampleUrl}`}\n`;
|
|
6222
6225
|
if (isConvex) instructions += `\n${pc.yellow("IMPORTANT:")} When using local development with Convex and native apps,\n ensure you use your local IP address instead of localhost or 127.0.0.1\n for proper connectivity.\n`;
|
|
6226
|
+
if (frontend.includes("native-unistyles")) instructions += `\n${pc.yellow("NOTE:")} Unistyles requires a development build.\n cd apps/native and run ${runCmd} android or ${runCmd} ios\n`;
|
|
6223
6227
|
return instructions;
|
|
6224
6228
|
}
|
|
6225
6229
|
function getHuskyInstructions(runCmd) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.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
|
"keywords": [
|
|
6
6
|
"better-auth",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"prepublishOnly": "npm run build"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@better-t-stack/types": "3.
|
|
70
|
+
"@better-t-stack/types": "^3.10.0",
|
|
71
71
|
"@clack/prompts": "^1.0.0-alpha.8",
|
|
72
72
|
"@orpc/server": "^1.12.2",
|
|
73
73
|
"consola": "^3.4.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
|
|
2
2
|
{{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
|
|
3
|
-
import { convex
|
|
3
|
+
import { convex } from "@convex-dev/better-auth/plugins";
|
|
4
4
|
import { expo } from "@better-auth/expo";
|
|
5
5
|
{{else if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
|
|
6
6
|
import { convex, crossDomain } from "@convex-dev/better-auth/plugins";
|
|
@@ -21,7 +21,6 @@ const siteUrl = process.env.SITE_URL!;
|
|
|
21
21
|
{{/if}}
|
|
22
22
|
{{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
|
|
23
23
|
const nativeAppUrl = process.env.NATIVE_APP_URL || "mybettertapp://";
|
|
24
|
-
const nativeSiteUrl = process.env.NATIVE_SITE_URL || "http://localhost:8081";
|
|
25
24
|
{{/if}}
|
|
26
25
|
|
|
27
26
|
export const authComponent = createClient<DataModel>(components.betterAuth);
|
|
@@ -30,11 +29,11 @@ function createAuth(ctx: GenericCtx<DataModel>) {
|
|
|
30
29
|
return betterAuth({
|
|
31
30
|
{{#if (and (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles")) (or (includes frontend "tanstack-start") (includes frontend "next")))}}
|
|
32
31
|
baseURL: siteUrl,
|
|
33
|
-
trustedOrigins: [siteUrl,
|
|
32
|
+
trustedOrigins: [siteUrl, nativeAppUrl],
|
|
34
33
|
{{else if (and (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles")) (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid")))}}
|
|
35
|
-
trustedOrigins: [siteUrl,
|
|
34
|
+
trustedOrigins: [siteUrl, nativeAppUrl],
|
|
36
35
|
{{else if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
|
|
37
|
-
trustedOrigins: [
|
|
36
|
+
trustedOrigins: [nativeAppUrl],
|
|
38
37
|
{{else if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
|
|
39
38
|
baseURL: siteUrl,
|
|
40
39
|
trustedOrigins: [siteUrl],
|
|
@@ -49,7 +48,6 @@ function createAuth(ctx: GenericCtx<DataModel>) {
|
|
|
49
48
|
plugins: [
|
|
50
49
|
{{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
|
|
51
50
|
expo(),
|
|
52
|
-
crossDomain({ siteUrl: nativeSiteUrl }),
|
|
53
51
|
{{else if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
|
|
54
52
|
crossDomain({ siteUrl }),
|
|
55
53
|
{{/if}}
|
|
@@ -3,14 +3,10 @@ import { authComponent, createAuth } from "./auth";
|
|
|
3
3
|
|
|
4
4
|
const http = httpRouter();
|
|
5
5
|
|
|
6
|
-
{{#if (or (includes frontend "tanstack-
|
|
7
|
-
{{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
|
|
6
|
+
{{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
|
|
8
7
|
authComponent.registerRoutes(http, createAuth, { cors: true });
|
|
9
8
|
{{else}}
|
|
10
9
|
authComponent.registerRoutes(http, createAuth);
|
|
11
10
|
{{/if}}
|
|
12
|
-
{{else}}
|
|
13
|
-
authComponent.registerRoutes(http, createAuth, { cors: true });
|
|
14
|
-
{{/if}}
|
|
15
11
|
|
|
16
12
|
export default http;
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import { createAuthClient } from "better-auth/react";
|
|
2
|
-
import { convexClient
|
|
2
|
+
import { convexClient } from "@convex-dev/better-auth/client/plugins";
|
|
3
3
|
import { expoClient } from "@better-auth/expo/client";
|
|
4
4
|
import Constants from "expo-constants";
|
|
5
5
|
import * as SecureStore from "expo-secure-store";
|
|
6
|
-
import { Platform } from "react-native";
|
|
7
6
|
|
|
8
7
|
export const authClient = createAuthClient({
|
|
9
8
|
baseURL: process.env.EXPO_PUBLIC_CONVEX_SITE_URL,
|
|
10
9
|
plugins: [
|
|
10
|
+
expoClient({
|
|
11
|
+
scheme: Constants.expoConfig?.scheme as string,
|
|
12
|
+
storagePrefix: Constants.expoConfig?.scheme as string,
|
|
13
|
+
storage: SecureStore,
|
|
14
|
+
}),
|
|
11
15
|
convexClient(),
|
|
12
|
-
...(Platform.OS === "web"
|
|
13
|
-
? [crossDomainClient()]
|
|
14
|
-
: [
|
|
15
|
-
expoClient({
|
|
16
|
-
scheme: Constants.expoConfig?.scheme as string,
|
|
17
|
-
storagePrefix: Constants.expoConfig?.scheme as string,
|
|
18
|
-
storage: SecureStore,
|
|
19
|
-
}),
|
|
20
|
-
]),
|
|
21
16
|
],
|
|
22
17
|
});
|
|
@@ -19,30 +19,27 @@ export default function UserMenu() {
|
|
|
19
19
|
|
|
20
20
|
return (
|
|
21
21
|
<DropdownMenu>
|
|
22
|
-
<DropdownMenuTrigger>
|
|
23
|
-
|
|
22
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
23
|
+
{user?.name}
|
|
24
24
|
</DropdownMenuTrigger>
|
|
25
25
|
<DropdownMenuContent className="bg-card">
|
|
26
26
|
<DropdownMenuGroup>
|
|
27
27
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
28
28
|
<DropdownMenuSeparator />
|
|
29
29
|
<DropdownMenuItem>{user?.email}</DropdownMenuItem>
|
|
30
|
-
<DropdownMenuItem
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
onSuccess: () => {
|
|
38
|
-
router.push("/dashboard");
|
|
39
|
-
},
|
|
30
|
+
<DropdownMenuItem
|
|
31
|
+
variant="destructive"
|
|
32
|
+
onClick={() => {
|
|
33
|
+
authClient.signOut({
|
|
34
|
+
fetchOptions: {
|
|
35
|
+
onSuccess: () => {
|
|
36
|
+
router.push("/dashboard");
|
|
40
37
|
},
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
Sign Out
|
|
46
43
|
</DropdownMenuItem>
|
|
47
44
|
</DropdownMenuGroup>
|
|
48
45
|
</DropdownMenuContent>
|
package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
DropdownMenu,
|
|
3
5
|
DropdownMenuContent,
|
|
@@ -8,43 +10,40 @@ import {
|
|
|
8
10
|
DropdownMenuTrigger,
|
|
9
11
|
} from "@/components/ui/dropdown-menu";
|
|
10
12
|
import { authClient } from "@/lib/auth-client";
|
|
11
|
-
import { useNavigate } from "@tanstack/react-router";
|
|
12
|
-
import { Button } from "./ui/button";
|
|
13
13
|
import { useQuery } from "convex/react";
|
|
14
14
|
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
15
15
|
|
|
16
|
+
import { Button } from "./ui/button";
|
|
17
|
+
|
|
16
18
|
export default function UserMenu() {
|
|
17
19
|
const navigate = useNavigate();
|
|
18
20
|
const user = useQuery(api.auth.getCurrentUser)
|
|
19
21
|
|
|
20
22
|
return (
|
|
21
23
|
<DropdownMenu>
|
|
22
|
-
<DropdownMenuTrigger>
|
|
23
|
-
|
|
24
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
25
|
+
{user?.name}
|
|
24
26
|
</DropdownMenuTrigger>
|
|
25
27
|
<DropdownMenuContent className="bg-card">
|
|
26
28
|
<DropdownMenuGroup>
|
|
27
29
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
28
30
|
<DropdownMenuSeparator />
|
|
29
31
|
<DropdownMenuItem>{user?.email}</DropdownMenuItem>
|
|
30
|
-
<DropdownMenuItem
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
to: "/dashboard",
|
|
40
|
-
});
|
|
41
|
-
},
|
|
32
|
+
<DropdownMenuItem
|
|
33
|
+
variant="destructive"
|
|
34
|
+
onClick={() => {
|
|
35
|
+
authClient.signOut({
|
|
36
|
+
fetchOptions: {
|
|
37
|
+
onSuccess: () => {
|
|
38
|
+
navigate({
|
|
39
|
+
to: "/dashboard",
|
|
40
|
+
});
|
|
42
41
|
},
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
Sign Out
|
|
48
47
|
</DropdownMenuItem>
|
|
49
48
|
</DropdownMenuGroup>
|
|
50
49
|
</DropdownMenuContent>
|
package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs
CHANGED
|
@@ -8,39 +8,37 @@ import {
|
|
|
8
8
|
DropdownMenuTrigger,
|
|
9
9
|
} from "@/components/ui/dropdown-menu";
|
|
10
10
|
import { authClient } from "@/lib/auth-client";
|
|
11
|
-
import { Button } from "./ui/button";
|
|
12
11
|
import { useQuery } from "convex/react";
|
|
13
12
|
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
14
13
|
|
|
14
|
+
import { Button } from "./ui/button";
|
|
15
|
+
|
|
15
16
|
export default function UserMenu() {
|
|
16
17
|
const user = useQuery(api.auth.getCurrentUser)
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
20
|
<DropdownMenu>
|
|
20
|
-
<DropdownMenuTrigger>
|
|
21
|
-
|
|
21
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
22
|
+
{user?.name}
|
|
22
23
|
</DropdownMenuTrigger>
|
|
23
24
|
<DropdownMenuContent className="bg-card">
|
|
24
25
|
<DropdownMenuGroup>
|
|
25
26
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
26
27
|
<DropdownMenuSeparator />
|
|
27
28
|
<DropdownMenuItem>{user?.email}</DropdownMenuItem>
|
|
28
|
-
<DropdownMenuItem
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
onSuccess: () => {
|
|
36
|
-
location.reload();
|
|
37
|
-
},
|
|
29
|
+
<DropdownMenuItem
|
|
30
|
+
variant="destructive"
|
|
31
|
+
onClick={() => {
|
|
32
|
+
authClient.signOut({
|
|
33
|
+
fetchOptions: {
|
|
34
|
+
onSuccess: () => {
|
|
35
|
+
location.reload();
|
|
38
36
|
},
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
Sign Out
|
|
44
42
|
</DropdownMenuItem>
|
|
45
43
|
</DropdownMenuGroup>
|
|
46
44
|
</DropdownMenuContent>
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { auth } from '@{{projectName}}/auth'
|
|
2
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
3
3
|
|
|
4
|
-
export const Route = createFileRoute(
|
|
4
|
+
export const Route = createFileRoute('/api/auth/$')({
|
|
5
5
|
server: {
|
|
6
6
|
handlers: {
|
|
7
|
-
GET: ({ request }) =>
|
|
8
|
-
|
|
7
|
+
GET: ({ request }) => {
|
|
8
|
+
return auth.handler(request)
|
|
9
|
+
},
|
|
10
|
+
POST: ({ request }) => {
|
|
11
|
+
return auth.handler(request)
|
|
12
|
+
},
|
|
9
13
|
},
|
|
10
14
|
},
|
|
11
|
-
})
|
|
15
|
+
})
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { useRouter } from "next/navigation";
|
|
3
|
+
|
|
1
4
|
import {
|
|
2
5
|
DropdownMenu,
|
|
3
6
|
DropdownMenuContent,
|
|
@@ -8,10 +11,9 @@ import {
|
|
|
8
11
|
DropdownMenuTrigger,
|
|
9
12
|
} from "@/components/ui/dropdown-menu";
|
|
10
13
|
import { authClient } from "@/lib/auth-client";
|
|
14
|
+
|
|
11
15
|
import { Button } from "./ui/button";
|
|
12
16
|
import { Skeleton } from "./ui/skeleton";
|
|
13
|
-
import { useRouter } from "next/navigation";
|
|
14
|
-
import Link from "next/link";
|
|
15
17
|
|
|
16
18
|
export default function UserMenu() {
|
|
17
19
|
const router = useRouter();
|
|
@@ -23,38 +25,35 @@ export default function UserMenu() {
|
|
|
23
25
|
|
|
24
26
|
if (!session) {
|
|
25
27
|
return (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
</
|
|
28
|
+
<Link href="/login">
|
|
29
|
+
<Button variant="outline">Sign In</Button>
|
|
30
|
+
</Link>
|
|
29
31
|
);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
return (
|
|
33
35
|
<DropdownMenu>
|
|
34
|
-
<DropdownMenuTrigger>
|
|
35
|
-
|
|
36
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
37
|
+
{session.user.name}
|
|
36
38
|
</DropdownMenuTrigger>
|
|
37
39
|
<DropdownMenuContent className="bg-card">
|
|
38
40
|
<DropdownMenuGroup>
|
|
39
41
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
40
42
|
<DropdownMenuSeparator />
|
|
41
43
|
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
|
|
42
|
-
<DropdownMenuItem
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
onSuccess: () => {
|
|
50
|
-
router.push("/");
|
|
51
|
-
},
|
|
44
|
+
<DropdownMenuItem
|
|
45
|
+
variant="destructive"
|
|
46
|
+
onClick={() => {
|
|
47
|
+
authClient.signOut({
|
|
48
|
+
fetchOptions: {
|
|
49
|
+
onSuccess: () => {
|
|
50
|
+
router.push("/");
|
|
52
51
|
},
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
Sign Out
|
|
58
57
|
</DropdownMenuItem>
|
|
59
58
|
</DropdownMenuGroup>
|
|
60
59
|
</DropdownMenuContent>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Link, useNavigate } from "react-router";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
DropdownMenu,
|
|
3
5
|
DropdownMenuContent,
|
|
@@ -8,10 +10,9 @@ import {
|
|
|
8
10
|
DropdownMenuTrigger,
|
|
9
11
|
} from "@/components/ui/dropdown-menu";
|
|
10
12
|
import { authClient } from "@/lib/auth-client";
|
|
11
|
-
|
|
13
|
+
|
|
12
14
|
import { Button } from "./ui/button";
|
|
13
15
|
import { Skeleton } from "./ui/skeleton";
|
|
14
|
-
import { Link } from "react-router";
|
|
15
16
|
|
|
16
17
|
export default function UserMenu() {
|
|
17
18
|
const navigate = useNavigate();
|
|
@@ -23,38 +24,35 @@ export default function UserMenu() {
|
|
|
23
24
|
|
|
24
25
|
if (!session) {
|
|
25
26
|
return (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
</
|
|
27
|
+
<Link to="/login">
|
|
28
|
+
<Button variant="outline">Sign In</Button>
|
|
29
|
+
</Link>
|
|
29
30
|
);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
return (
|
|
33
34
|
<DropdownMenu>
|
|
34
|
-
<DropdownMenuTrigger>
|
|
35
|
-
|
|
35
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
36
|
+
{session.user.name}
|
|
36
37
|
</DropdownMenuTrigger>
|
|
37
38
|
<DropdownMenuContent className="bg-card">
|
|
38
39
|
<DropdownMenuGroup>
|
|
39
40
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
40
41
|
<DropdownMenuSeparator />
|
|
41
42
|
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
|
|
42
|
-
<DropdownMenuItem
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
onSuccess: () => {
|
|
50
|
-
navigate("/");
|
|
51
|
-
},
|
|
43
|
+
<DropdownMenuItem
|
|
44
|
+
variant="destructive"
|
|
45
|
+
onClick={() => {
|
|
46
|
+
authClient.signOut({
|
|
47
|
+
fetchOptions: {
|
|
48
|
+
onSuccess: () => {
|
|
49
|
+
navigate("/");
|
|
52
50
|
},
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
Sign Out
|
|
58
56
|
</DropdownMenuItem>
|
|
59
57
|
</DropdownMenuGroup>
|
|
60
58
|
</DropdownMenuContent>
|
package/templates/auth/better-auth/web/react/tanstack-router/src/components/user-menu.tsx.hbs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
DropdownMenu,
|
|
3
5
|
DropdownMenuContent,
|
|
@@ -8,10 +10,9 @@ import {
|
|
|
8
10
|
DropdownMenuTrigger,
|
|
9
11
|
} from "@/components/ui/dropdown-menu";
|
|
10
12
|
import { authClient } from "@/lib/auth-client";
|
|
11
|
-
|
|
13
|
+
|
|
12
14
|
import { Button } from "./ui/button";
|
|
13
15
|
import { Skeleton } from "./ui/skeleton";
|
|
14
|
-
import { Link } from "@tanstack/react-router";
|
|
15
16
|
|
|
16
17
|
export default function UserMenu() {
|
|
17
18
|
const navigate = useNavigate();
|
|
@@ -23,40 +24,37 @@ export default function UserMenu() {
|
|
|
23
24
|
|
|
24
25
|
if (!session) {
|
|
25
26
|
return (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
</
|
|
27
|
+
<Link to="/login">
|
|
28
|
+
<Button variant="outline">Sign In</Button>
|
|
29
|
+
</Link>
|
|
29
30
|
);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
return (
|
|
33
34
|
<DropdownMenu>
|
|
34
|
-
<DropdownMenuTrigger>
|
|
35
|
-
|
|
35
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
36
|
+
{session.user.name}
|
|
36
37
|
</DropdownMenuTrigger>
|
|
37
38
|
<DropdownMenuContent className="bg-card">
|
|
38
39
|
<DropdownMenuGroup>
|
|
39
40
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
40
41
|
<DropdownMenuSeparator />
|
|
41
42
|
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
|
|
42
|
-
<DropdownMenuItem
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
to: "/",
|
|
52
|
-
});
|
|
53
|
-
},
|
|
43
|
+
<DropdownMenuItem
|
|
44
|
+
variant="destructive"
|
|
45
|
+
onClick={() => {
|
|
46
|
+
authClient.signOut({
|
|
47
|
+
fetchOptions: {
|
|
48
|
+
onSuccess: () => {
|
|
49
|
+
navigate({
|
|
50
|
+
to: "/",
|
|
51
|
+
});
|
|
54
52
|
},
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
Sign Out
|
|
60
58
|
</DropdownMenuItem>
|
|
61
59
|
</DropdownMenuGroup>
|
|
62
60
|
</DropdownMenuContent>
|
package/templates/auth/better-auth/web/react/tanstack-start/src/components/user-menu.tsx.hbs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
DropdownMenu,
|
|
3
5
|
DropdownMenuContent,
|
|
@@ -8,10 +10,9 @@ import {
|
|
|
8
10
|
DropdownMenuTrigger,
|
|
9
11
|
} from "@/components/ui/dropdown-menu";
|
|
10
12
|
import { authClient } from "@/lib/auth-client";
|
|
11
|
-
|
|
13
|
+
|
|
12
14
|
import { Button } from "./ui/button";
|
|
13
15
|
import { Skeleton } from "./ui/skeleton";
|
|
14
|
-
import { Link } from "@tanstack/react-router";
|
|
15
16
|
|
|
16
17
|
export default function UserMenu() {
|
|
17
18
|
const navigate = useNavigate();
|
|
@@ -23,40 +24,37 @@ export default function UserMenu() {
|
|
|
23
24
|
|
|
24
25
|
if (!session) {
|
|
25
26
|
return (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
</
|
|
27
|
+
<Link to="/login">
|
|
28
|
+
<Button variant="outline">Sign In</Button>
|
|
29
|
+
</Link>
|
|
29
30
|
);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
return (
|
|
33
34
|
<DropdownMenu>
|
|
34
|
-
<DropdownMenuTrigger>
|
|
35
|
-
|
|
35
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
36
|
+
{session.user.name}
|
|
36
37
|
</DropdownMenuTrigger>
|
|
37
38
|
<DropdownMenuContent className="bg-card">
|
|
38
39
|
<DropdownMenuGroup>
|
|
39
40
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
40
41
|
<DropdownMenuSeparator />
|
|
41
42
|
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
|
|
42
|
-
<DropdownMenuItem
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
to: "/",
|
|
52
|
-
});
|
|
53
|
-
},
|
|
43
|
+
<DropdownMenuItem
|
|
44
|
+
variant="destructive"
|
|
45
|
+
onClick={() => {
|
|
46
|
+
authClient.signOut({
|
|
47
|
+
fetchOptions: {
|
|
48
|
+
onSuccess: () => {
|
|
49
|
+
navigate({
|
|
50
|
+
to: "/",
|
|
51
|
+
});
|
|
54
52
|
},
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
Sign Out
|
|
60
58
|
</DropdownMenuItem>
|
|
61
59
|
</DropdownMenuGroup>
|
|
62
60
|
</DropdownMenuContent>
|
|
@@ -6,7 +6,7 @@ See https://docs.convex.dev/functions for more.
|
|
|
6
6
|
A query function that takes two arguments looks like:
|
|
7
7
|
|
|
8
8
|
```ts
|
|
9
|
-
//
|
|
9
|
+
// convex/myFunctions.ts
|
|
10
10
|
import { query } from "./_generated/server";
|
|
11
11
|
import { v } from "convex/values";
|
|
12
12
|
|
|
@@ -36,7 +36,7 @@ export const myQueryFunction = query({
|
|
|
36
36
|
Using this query function in a React component looks like:
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
|
-
const data = useQuery(api.
|
|
39
|
+
const data = useQuery(api.myFunctions.myQueryFunction, {
|
|
40
40
|
first: 10,
|
|
41
41
|
second: "hello",
|
|
42
42
|
});
|
|
@@ -45,7 +45,7 @@ const data = useQuery(api.functions.myQueryFunction, {
|
|
|
45
45
|
A mutation function looks like:
|
|
46
46
|
|
|
47
47
|
```ts
|
|
48
|
-
//
|
|
48
|
+
// convex/myFunctions.ts
|
|
49
49
|
import { mutation } from "./_generated/server";
|
|
50
50
|
import { v } from "convex/values";
|
|
51
51
|
|
|
@@ -73,7 +73,7 @@ export const myMutationFunction = mutation({
|
|
|
73
73
|
Using this mutation function in a React component looks like:
|
|
74
74
|
|
|
75
75
|
```ts
|
|
76
|
-
const mutation = useMutation(api.
|
|
76
|
+
const mutation = useMutation(api.myFunctions.myMutationFunction);
|
|
77
77
|
function handleButtonPress() {
|
|
78
78
|
// fire and forget, the most common way to use mutations
|
|
79
79
|
mutation({ first: "Hello!", second: "me" });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
/* This TypeScript project config describes the environment that
|
|
3
3
|
* Convex functions run in and is used to typecheck them.
|
|
4
|
-
* You can modify it, but some settings required to use Convex.
|
|
4
|
+
* You can modify it, but some settings are required to use Convex.
|
|
5
5
|
*/
|
|
6
6
|
"compilerOptions": {
|
|
7
7
|
/* These settings are not required by Convex and can be modified. */
|
|
@@ -16,12 +16,10 @@ export function ModeToggle() {
|
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<DropdownMenu>
|
|
19
|
-
<DropdownMenuTrigger
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<span className="sr-only">Toggle theme</span>
|
|
24
|
-
</Button>
|
|
19
|
+
<DropdownMenuTrigger render={<Button variant="outline" size="icon" />}>
|
|
20
|
+
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
21
|
+
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
22
|
+
<span className="sr-only">Toggle theme</span>
|
|
25
23
|
</DropdownMenuTrigger>
|
|
26
24
|
<DropdownMenuContent align="end">
|
|
27
25
|
<DropdownMenuItem onClick={() => setTheme("light")}>
|
|
@@ -14,12 +14,10 @@ export function ModeToggle() {
|
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
16
|
<DropdownMenu>
|
|
17
|
-
<DropdownMenuTrigger>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<span className="sr-only">Toggle theme</span>
|
|
22
|
-
</Button>
|
|
17
|
+
<DropdownMenuTrigger render={<Button variant="outline" size="icon" />}>
|
|
18
|
+
<Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
|
|
19
|
+
<Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
|
|
20
|
+
<span className="sr-only">Toggle theme</span>
|
|
23
21
|
</DropdownMenuTrigger>
|
|
24
22
|
<DropdownMenuContent align="end">
|
|
25
23
|
<DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
|
|
@@ -14,12 +14,10 @@ export function ModeToggle() {
|
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
16
|
<DropdownMenu>
|
|
17
|
-
<DropdownMenuTrigger>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<span className="sr-only">Toggle theme</span>
|
|
22
|
-
</Button>
|
|
17
|
+
<DropdownMenuTrigger render={<Button variant="outline" size="icon" />}>
|
|
18
|
+
<Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
|
|
19
|
+
<Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
|
|
20
|
+
<span className="sr-only">Toggle theme</span>
|
|
23
21
|
</DropdownMenuTrigger>
|
|
24
22
|
<DropdownMenuContent align="end">
|
|
25
23
|
<DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
|