@better-t-stack/template-generator 3.25.4 → 3.26.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/index.d.mts +10 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2649 -544
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -646,7 +646,7 @@ function updateEnvPackageJson(vfs, config) {
|
|
|
646
646
|
const pkgJson = vfs.readJson("packages/env/package.json");
|
|
647
647
|
if (!pkgJson) return;
|
|
648
648
|
pkgJson.name = `@${config.projectName}/env`;
|
|
649
|
-
const hasWebFrontend = config.frontend.some((f) => desktopWebFrontends.includes(f));
|
|
649
|
+
const hasWebFrontend$1 = config.frontend.some((f) => desktopWebFrontends.includes(f));
|
|
650
650
|
const hasNative = config.frontend.some((f) => [
|
|
651
651
|
"native-bare",
|
|
652
652
|
"native-uniwind",
|
|
@@ -655,7 +655,7 @@ function updateEnvPackageJson(vfs, config) {
|
|
|
655
655
|
const needsServerEnv = config.backend !== "none" && config.backend !== "convex";
|
|
656
656
|
const exports = {};
|
|
657
657
|
if (needsServerEnv) exports["./server"] = "./src/server.ts";
|
|
658
|
-
if (hasWebFrontend) exports["./web"] = "./src/web.ts";
|
|
658
|
+
if (hasWebFrontend$1) exports["./web"] = "./src/web.ts";
|
|
659
659
|
if (hasNative) exports["./native"] = "./src/native.ts";
|
|
660
660
|
pkgJson.exports = exports;
|
|
661
661
|
vfs.writeJson("packages/env/package.json", pkgJson);
|
|
@@ -707,10 +707,14 @@ const dependencyVersionMap = {
|
|
|
707
707
|
typescript: "^5",
|
|
708
708
|
"better-auth": "1.5.2",
|
|
709
709
|
"@better-auth/expo": "1.5.2",
|
|
710
|
-
"@clerk/
|
|
711
|
-
"@clerk/
|
|
712
|
-
"@clerk/
|
|
713
|
-
"@clerk/
|
|
710
|
+
"@clerk/backend": "^3.2.1",
|
|
711
|
+
"@clerk/express": "^2.0.5",
|
|
712
|
+
"@clerk/fastify": "^3.1.3",
|
|
713
|
+
"@clerk/nextjs": "^7.0.5",
|
|
714
|
+
"@clerk/react": "^6.1.1",
|
|
715
|
+
"@clerk/react-router": "^3.0.5",
|
|
716
|
+
"@clerk/tanstack-react-start": "^1.0.5",
|
|
717
|
+
"@clerk/expo": "^3.1.3",
|
|
714
718
|
"drizzle-orm": "^0.45.1",
|
|
715
719
|
"drizzle-kit": "^0.31.8",
|
|
716
720
|
"@planetscale/database": "^1.19.0",
|
|
@@ -775,7 +779,7 @@ const dependencyVersionMap = {
|
|
|
775
779
|
"@trpc/tanstack-react-query": "^11.7.2",
|
|
776
780
|
"@trpc/server": "^11.7.2",
|
|
777
781
|
"@trpc/client": "^11.7.2",
|
|
778
|
-
next: "^16.
|
|
782
|
+
next: "^16.2.0",
|
|
779
783
|
convex: "^1.32.0",
|
|
780
784
|
"@convex-dev/react-query": "^0.1.0",
|
|
781
785
|
"@convex-dev/agent": "^0.3.2",
|
|
@@ -1351,8 +1355,10 @@ function processConvexAuthDeps(vfs, config) {
|
|
|
1351
1355
|
"native-unistyles"
|
|
1352
1356
|
].includes(f));
|
|
1353
1357
|
const hasNextJs = frontend.includes("next");
|
|
1358
|
+
const hasReactRouter = frontend.includes("react-router");
|
|
1359
|
+
const hasTanStackRouter = frontend.includes("tanstack-router");
|
|
1354
1360
|
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
1355
|
-
const hasViteReact =
|
|
1361
|
+
const hasViteReact = hasReactRouter || hasTanStackRouter;
|
|
1356
1362
|
const hasSolid = frontend.includes("solid");
|
|
1357
1363
|
const hasSvelte = frontend.includes("svelte");
|
|
1358
1364
|
const hasReactWebAuthForms = hasNextJs || hasTanStackStart || hasViteReact;
|
|
@@ -1363,21 +1369,26 @@ function processConvexAuthDeps(vfs, config) {
|
|
|
1363
1369
|
packagePath: webPath,
|
|
1364
1370
|
dependencies: ["@clerk/nextjs"]
|
|
1365
1371
|
});
|
|
1366
|
-
else if (
|
|
1372
|
+
else if (hasReactRouter) addPackageDependency({
|
|
1367
1373
|
vfs,
|
|
1368
1374
|
packagePath: webPath,
|
|
1369
|
-
dependencies: ["@clerk/
|
|
1375
|
+
dependencies: ["@clerk/react-router"]
|
|
1370
1376
|
});
|
|
1371
|
-
else if (
|
|
1377
|
+
else if (hasTanStackRouter) addPackageDependency({
|
|
1378
|
+
vfs,
|
|
1379
|
+
packagePath: webPath,
|
|
1380
|
+
dependencies: ["@clerk/react"]
|
|
1381
|
+
});
|
|
1382
|
+
else if (hasTanStackStart) addPackageDependency({
|
|
1372
1383
|
vfs,
|
|
1373
1384
|
packagePath: webPath,
|
|
1374
|
-
dependencies: ["@clerk/
|
|
1385
|
+
dependencies: ["@clerk/tanstack-react-start"]
|
|
1375
1386
|
});
|
|
1376
1387
|
}
|
|
1377
1388
|
if (nativeExists && hasNative) addPackageDependency({
|
|
1378
1389
|
vfs,
|
|
1379
1390
|
packagePath: nativePath,
|
|
1380
|
-
dependencies: ["@clerk/
|
|
1391
|
+
dependencies: ["@clerk/expo"]
|
|
1381
1392
|
});
|
|
1382
1393
|
} else if (auth === "better-auth") {
|
|
1383
1394
|
if (backendExists) {
|
|
@@ -1434,19 +1445,23 @@ function processConvexAuthDeps(vfs, config) {
|
|
|
1434
1445
|
}
|
|
1435
1446
|
}
|
|
1436
1447
|
function processStandardAuthDeps(vfs, config) {
|
|
1437
|
-
const { auth, frontend } = config;
|
|
1448
|
+
const { auth, backend, frontend } = config;
|
|
1438
1449
|
const authPath = "packages/auth/package.json";
|
|
1450
|
+
const apiPath = "packages/api/package.json";
|
|
1439
1451
|
const webPath = "apps/web/package.json";
|
|
1440
1452
|
const nativePath = "apps/native/package.json";
|
|
1453
|
+
const serverPath = "apps/server/package.json";
|
|
1441
1454
|
const authExists = vfs.exists(authPath);
|
|
1455
|
+
const apiExists = vfs.exists(apiPath);
|
|
1442
1456
|
const webExists = vfs.exists(webPath);
|
|
1443
1457
|
const nativeExists = vfs.exists(nativePath);
|
|
1458
|
+
const serverExists = vfs.exists(serverPath);
|
|
1444
1459
|
const hasNative = frontend.some((f) => [
|
|
1445
1460
|
"native-bare",
|
|
1446
1461
|
"native-uniwind",
|
|
1447
1462
|
"native-unistyles"
|
|
1448
1463
|
].includes(f));
|
|
1449
|
-
const hasWebFrontend = frontend.some((f) => [
|
|
1464
|
+
const hasWebFrontend$1 = frontend.some((f) => [
|
|
1450
1465
|
"react-router",
|
|
1451
1466
|
"tanstack-router",
|
|
1452
1467
|
"tanstack-start",
|
|
@@ -1464,7 +1479,68 @@ function processStandardAuthDeps(vfs, config) {
|
|
|
1464
1479
|
].includes(f));
|
|
1465
1480
|
const hasSolid = frontend.includes("solid");
|
|
1466
1481
|
const hasSvelte = frontend.includes("svelte");
|
|
1467
|
-
|
|
1482
|
+
const hasNextJs = frontend.includes("next");
|
|
1483
|
+
const hasReactRouter = frontend.includes("react-router");
|
|
1484
|
+
const hasTanStackRouter = frontend.includes("tanstack-router");
|
|
1485
|
+
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
1486
|
+
if (auth === "clerk") {
|
|
1487
|
+
if (webExists) {
|
|
1488
|
+
if (hasNextJs) addPackageDependency({
|
|
1489
|
+
vfs,
|
|
1490
|
+
packagePath: webPath,
|
|
1491
|
+
dependencies: ["@clerk/nextjs"]
|
|
1492
|
+
});
|
|
1493
|
+
else if (hasReactRouter) addPackageDependency({
|
|
1494
|
+
vfs,
|
|
1495
|
+
packagePath: webPath,
|
|
1496
|
+
dependencies: ["@clerk/react-router"]
|
|
1497
|
+
});
|
|
1498
|
+
else if (hasTanStackRouter) addPackageDependency({
|
|
1499
|
+
vfs,
|
|
1500
|
+
packagePath: webPath,
|
|
1501
|
+
dependencies: ["@clerk/react"]
|
|
1502
|
+
});
|
|
1503
|
+
else if (hasTanStackStart) addPackageDependency({
|
|
1504
|
+
vfs,
|
|
1505
|
+
packagePath: webPath,
|
|
1506
|
+
dependencies: ["@clerk/tanstack-react-start"]
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
if (hasNative && nativeExists) addPackageDependency({
|
|
1510
|
+
vfs,
|
|
1511
|
+
packagePath: nativePath,
|
|
1512
|
+
dependencies: ["@clerk/expo"]
|
|
1513
|
+
});
|
|
1514
|
+
if (apiExists) {
|
|
1515
|
+
if (backend === "self" || backend === "hono" || backend === "elysia") addPackageDependency({
|
|
1516
|
+
vfs,
|
|
1517
|
+
packagePath: apiPath,
|
|
1518
|
+
dependencies: ["@clerk/backend"]
|
|
1519
|
+
});
|
|
1520
|
+
else if (backend === "express") addPackageDependency({
|
|
1521
|
+
vfs,
|
|
1522
|
+
packagePath: apiPath,
|
|
1523
|
+
dependencies: ["@clerk/express"]
|
|
1524
|
+
});
|
|
1525
|
+
else if (backend === "fastify") addPackageDependency({
|
|
1526
|
+
vfs,
|
|
1527
|
+
packagePath: apiPath,
|
|
1528
|
+
dependencies: ["@clerk/fastify"]
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
if (serverExists) {
|
|
1532
|
+
if (backend === "express") addPackageDependency({
|
|
1533
|
+
vfs,
|
|
1534
|
+
packagePath: serverPath,
|
|
1535
|
+
dependencies: ["@clerk/express"]
|
|
1536
|
+
});
|
|
1537
|
+
else if (backend === "fastify") addPackageDependency({
|
|
1538
|
+
vfs,
|
|
1539
|
+
packagePath: serverPath,
|
|
1540
|
+
dependencies: ["@clerk/fastify"]
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
} else if (auth === "better-auth") {
|
|
1468
1544
|
if (authExists) {
|
|
1469
1545
|
addPackageDependency({
|
|
1470
1546
|
vfs,
|
|
@@ -1477,7 +1553,7 @@ function processStandardAuthDeps(vfs, config) {
|
|
|
1477
1553
|
dependencies: ["@better-auth/expo"]
|
|
1478
1554
|
});
|
|
1479
1555
|
}
|
|
1480
|
-
if (hasWebFrontend && webExists) {
|
|
1556
|
+
if (hasWebFrontend$1 && webExists) {
|
|
1481
1557
|
addPackageDependency({
|
|
1482
1558
|
vfs,
|
|
1483
1559
|
packagePath: webPath,
|
|
@@ -1907,12 +1983,8 @@ function buildClientVars(frontend, backend, auth) {
|
|
|
1907
1983
|
value: backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value,
|
|
1908
1984
|
condition: backend === "convex" ? true : baseVar.write
|
|
1909
1985
|
}];
|
|
1910
|
-
if (
|
|
1986
|
+
if (auth === "clerk") {
|
|
1911
1987
|
if (hasNextJs) vars.push({
|
|
1912
|
-
key: "NEXT_PUBLIC_CLERK_FRONTEND_API_URL",
|
|
1913
|
-
value: "",
|
|
1914
|
-
condition: true
|
|
1915
|
-
}, {
|
|
1916
1988
|
key: "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",
|
|
1917
1989
|
value: "",
|
|
1918
1990
|
condition: true
|
|
@@ -1927,7 +1999,7 @@ function buildClientVars(frontend, backend, auth) {
|
|
|
1927
1999
|
value: "",
|
|
1928
2000
|
condition: true
|
|
1929
2001
|
});
|
|
1930
|
-
if (hasTanStackStart) vars.push({
|
|
2002
|
+
if (hasReactRouter || hasTanStackStart) vars.push({
|
|
1931
2003
|
key: "CLERK_SECRET_KEY",
|
|
1932
2004
|
value: "",
|
|
1933
2005
|
condition: true
|
|
@@ -1962,7 +2034,7 @@ function buildNativeVars(frontend, backend, auth) {
|
|
|
1962
2034
|
value: serverUrl,
|
|
1963
2035
|
condition: true
|
|
1964
2036
|
}];
|
|
1965
|
-
if (
|
|
2037
|
+
if (auth === "clerk") vars.push({
|
|
1966
2038
|
key: "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY",
|
|
1967
2039
|
value: "",
|
|
1968
2040
|
condition: true
|
|
@@ -2027,7 +2099,7 @@ function buildConvexCommentBlocks(frontend, auth, examples) {
|
|
|
2027
2099
|
${hasWeb || hasNative ? `# npx convex env set SITE_URL ${defaultSiteUrl}\n` : ""}`;
|
|
2028
2100
|
return commentBlocks;
|
|
2029
2101
|
}
|
|
2030
|
-
function buildServerVars(backend, frontend, auth, database, dbSetup, runtime, webDeploy, serverDeploy, payments, examples) {
|
|
2102
|
+
function buildServerVars(backend, frontend, auth, api, database, dbSetup, runtime, webDeploy, serverDeploy, payments, examples) {
|
|
2031
2103
|
const hasReactRouter = frontend.includes("react-router");
|
|
2032
2104
|
const hasSvelte = frontend.includes("svelte");
|
|
2033
2105
|
const hasAstro = frontend.includes("astro");
|
|
@@ -2052,6 +2124,12 @@ function buildServerVars(backend, frontend, auth, database, dbSetup, runtime, we
|
|
|
2052
2124
|
break;
|
|
2053
2125
|
}
|
|
2054
2126
|
const hasBetterAuth = auth === "better-auth";
|
|
2127
|
+
const hasClerk = auth === "clerk";
|
|
2128
|
+
const needsClerkPublishableKey = hasClerk && (["express", "fastify"].includes(backend) || api !== "none" && [
|
|
2129
|
+
"self",
|
|
2130
|
+
"hono",
|
|
2131
|
+
"elysia"
|
|
2132
|
+
].includes(backend));
|
|
2055
2133
|
return [
|
|
2056
2134
|
{
|
|
2057
2135
|
key: "BETTER_AUTH_SECRET",
|
|
@@ -2063,6 +2141,16 @@ function buildServerVars(backend, frontend, auth, database, dbSetup, runtime, we
|
|
|
2063
2141
|
value: backend === "self" ? hasAstro ? "http://localhost:4321" : "http://localhost:3001" : "http://localhost:3000",
|
|
2064
2142
|
condition: hasBetterAuth
|
|
2065
2143
|
},
|
|
2144
|
+
{
|
|
2145
|
+
key: "CLERK_SECRET_KEY",
|
|
2146
|
+
value: "",
|
|
2147
|
+
condition: hasClerk
|
|
2148
|
+
},
|
|
2149
|
+
{
|
|
2150
|
+
key: "CLERK_PUBLISHABLE_KEY",
|
|
2151
|
+
value: "",
|
|
2152
|
+
condition: needsClerkPublishableKey
|
|
2153
|
+
},
|
|
2066
2154
|
{
|
|
2067
2155
|
key: "POLAR_ACCESS_TOKEN",
|
|
2068
2156
|
value: "",
|
|
@@ -2091,7 +2179,7 @@ function buildServerVars(backend, frontend, auth, database, dbSetup, runtime, we
|
|
|
2091
2179
|
];
|
|
2092
2180
|
}
|
|
2093
2181
|
function processEnvVariables(vfs, config) {
|
|
2094
|
-
const { backend, frontend, database, auth, examples, dbSetup, webDeploy, serverDeploy, runtime, payments } = config;
|
|
2182
|
+
const { backend, frontend, database, auth, api, examples, dbSetup, webDeploy, serverDeploy, runtime, payments } = config;
|
|
2095
2183
|
const hasReactRouter = frontend.includes("react-router");
|
|
2096
2184
|
const hasTanStackRouter = frontend.includes("tanstack-router");
|
|
2097
2185
|
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
@@ -2128,7 +2216,7 @@ function processEnvVariables(vfs, config) {
|
|
|
2128
2216
|
}
|
|
2129
2217
|
return;
|
|
2130
2218
|
}
|
|
2131
|
-
const serverVars = buildServerVars(backend, frontend, auth, database, dbSetup, runtime, webDeploy, serverDeploy, payments, examples);
|
|
2219
|
+
const serverVars = buildServerVars(backend, frontend, auth, api, database, dbSetup, runtime, webDeploy, serverDeploy, payments, examples);
|
|
2132
2220
|
if (backend === "self") {
|
|
2133
2221
|
const webDir = "apps/web";
|
|
2134
2222
|
if (vfs.directoryExists(webDir)) writeEnvFile(vfs, `${webDir}/.env`, serverVars);
|
|
@@ -2448,6 +2536,77 @@ function getDesktopStaticBuildNote(frontend) {
|
|
|
2448
2536
|
if (!staticBuildFrontend) return "";
|
|
2449
2537
|
return `Desktop builds package static web assets. ${staticBuildFrontends.get(staticBuildFrontend)} needs a static/export build configuration before desktop packaging will work.`;
|
|
2450
2538
|
}
|
|
2539
|
+
function getClerkQuickstartUrl(frontend) {
|
|
2540
|
+
if (frontend.includes("next")) return "https://clerk.com/docs/nextjs/getting-started/quickstart";
|
|
2541
|
+
if (frontend.includes("react-router")) return "https://clerk.com/docs/react-router/getting-started/quickstart";
|
|
2542
|
+
if (frontend.includes("tanstack-start")) return "https://clerk.com/docs/tanstack-react-start/getting-started/quickstart";
|
|
2543
|
+
if (frontend.includes("tanstack-router")) return "https://clerk.com/docs/react/getting-started/quickstart";
|
|
2544
|
+
if (frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles")) return "https://clerk.com/docs/expo/getting-started/quickstart";
|
|
2545
|
+
return "https://clerk.com/docs";
|
|
2546
|
+
}
|
|
2547
|
+
function getClerkFrontendEnvLines(frontend) {
|
|
2548
|
+
const lines = [];
|
|
2549
|
+
if (frontend.includes("next")) lines.push("- Set `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` in `apps/web/.env`");
|
|
2550
|
+
if (frontend.some((value) => [
|
|
2551
|
+
"react-router",
|
|
2552
|
+
"tanstack-router",
|
|
2553
|
+
"tanstack-start"
|
|
2554
|
+
].includes(value))) lines.push("- Set `VITE_CLERK_PUBLISHABLE_KEY` in `apps/web/.env`");
|
|
2555
|
+
if (frontend.some((value) => [
|
|
2556
|
+
"native-bare",
|
|
2557
|
+
"native-uniwind",
|
|
2558
|
+
"native-unistyles"
|
|
2559
|
+
].includes(value))) lines.push("- Set `EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY` in `apps/native/.env`");
|
|
2560
|
+
return lines;
|
|
2561
|
+
}
|
|
2562
|
+
function getClerkSetupLines(frontend, backend, api, isConvex) {
|
|
2563
|
+
const lines = getClerkFrontendEnvLines(frontend);
|
|
2564
|
+
const hasClerkServerFrontend = frontend.some((value) => [
|
|
2565
|
+
"next",
|
|
2566
|
+
"react-router",
|
|
2567
|
+
"tanstack-start"
|
|
2568
|
+
].includes(value));
|
|
2569
|
+
if (isConvex) return [
|
|
2570
|
+
"- Set `CLERK_JWT_ISSUER_DOMAIN` in Convex Dashboard",
|
|
2571
|
+
...lines,
|
|
2572
|
+
...hasClerkServerFrontend ? ["- Set `CLERK_SECRET_KEY` in `apps/web/.env` for Clerk server middleware"] : []
|
|
2573
|
+
];
|
|
2574
|
+
const serverEnvPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
2575
|
+
const needsServerSideClerkAuth = backend !== "none";
|
|
2576
|
+
const needsClerkBackendPublishableKey = ["express", "fastify"].includes(backend);
|
|
2577
|
+
const needsClerkRequestVerification = api !== "none" && [
|
|
2578
|
+
"self",
|
|
2579
|
+
"hono",
|
|
2580
|
+
"elysia"
|
|
2581
|
+
].includes(backend);
|
|
2582
|
+
if (hasClerkServerFrontend && backend === "self") lines.push("- Set `CLERK_SECRET_KEY` in `apps/web/.env` for Clerk server middleware and server-side Clerk auth");
|
|
2583
|
+
else {
|
|
2584
|
+
if (hasClerkServerFrontend) lines.push("- Set `CLERK_SECRET_KEY` in `apps/web/.env` for Clerk server middleware");
|
|
2585
|
+
if (needsServerSideClerkAuth) lines.push(`- Set \`CLERK_SECRET_KEY\` in \`${serverEnvPath}\` for server-side Clerk auth`);
|
|
2586
|
+
}
|
|
2587
|
+
if (needsClerkRequestVerification) lines.push(`- Set \`CLERK_PUBLISHABLE_KEY\` in \`${serverEnvPath}\` for server-side Clerk request verification`);
|
|
2588
|
+
if (needsClerkBackendPublishableKey) lines.push(`- Set \`CLERK_PUBLISHABLE_KEY\` in \`${serverEnvPath}\` for Clerk backend middleware`);
|
|
2589
|
+
return lines;
|
|
2590
|
+
}
|
|
2591
|
+
function hasNativeFrontend(frontend) {
|
|
2592
|
+
return frontend.some((value) => [
|
|
2593
|
+
"native-bare",
|
|
2594
|
+
"native-uniwind",
|
|
2595
|
+
"native-unistyles"
|
|
2596
|
+
].includes(value));
|
|
2597
|
+
}
|
|
2598
|
+
function hasWebFrontend(frontend) {
|
|
2599
|
+
return frontend.some((value) => [
|
|
2600
|
+
"tanstack-router",
|
|
2601
|
+
"react-router",
|
|
2602
|
+
"tanstack-start",
|
|
2603
|
+
"next",
|
|
2604
|
+
"svelte",
|
|
2605
|
+
"nuxt",
|
|
2606
|
+
"solid",
|
|
2607
|
+
"astro"
|
|
2608
|
+
].includes(value));
|
|
2609
|
+
}
|
|
2451
2610
|
function processReadme(vfs, config) {
|
|
2452
2611
|
const content = generateReadmeContent(config);
|
|
2453
2612
|
vfs.writeFile("README.md", content);
|
|
@@ -2456,11 +2615,7 @@ function generateReadmeContent(options) {
|
|
|
2456
2615
|
const { projectName, packageManager, database, auth, addons = [], orm = "drizzle", runtime = "bun", frontend = ["tanstack-router"], backend = "hono", api = "trpc", webDeploy, serverDeploy } = options;
|
|
2457
2616
|
const isConvex = backend === "convex";
|
|
2458
2617
|
const hasReactRouter = frontend.includes("react-router");
|
|
2459
|
-
const hasNative = frontend
|
|
2460
|
-
"native-bare",
|
|
2461
|
-
"native-uniwind",
|
|
2462
|
-
"native-unistyles"
|
|
2463
|
-
].includes(f));
|
|
2618
|
+
const hasNative = hasNativeFrontend(frontend);
|
|
2464
2619
|
const hasReactWeb = frontend.some((f) => [
|
|
2465
2620
|
"tanstack-router",
|
|
2466
2621
|
"react-router",
|
|
@@ -2503,8 +2658,12 @@ ${auth === "clerk" ? `
|
|
|
2503
2658
|
### Clerk Authentication Setup
|
|
2504
2659
|
|
|
2505
2660
|
- Follow the guide: [Convex + Clerk](https://docs.convex.dev/auth/clerk)
|
|
2506
|
-
|
|
2507
|
-
|
|
2661
|
+
${getClerkSetupLines(frontend, backend, api, true).join("\n")}` : ""}` : generateDatabaseSetup(options, packageManagerRunCmd)}
|
|
2662
|
+
${!isConvex && auth === "clerk" ? `
|
|
2663
|
+
## Clerk Authentication Setup
|
|
2664
|
+
|
|
2665
|
+
- Follow the guide: [Clerk Quickstart](${getClerkQuickstartUrl(frontend)})
|
|
2666
|
+
${getClerkSetupLines(frontend, backend, api, false).join("\n")}` : ""}
|
|
2508
2667
|
|
|
2509
2668
|
Then, run the development server:
|
|
2510
2669
|
|
|
@@ -2539,7 +2698,10 @@ function generateStackDescription(frontend, backend, api, isConvex) {
|
|
|
2539
2698
|
svelte: "SvelteKit",
|
|
2540
2699
|
nuxt: "Nuxt",
|
|
2541
2700
|
solid: "SolidJS",
|
|
2542
|
-
astro: "Astro"
|
|
2701
|
+
astro: "Astro",
|
|
2702
|
+
"native-bare": "React Native, Expo",
|
|
2703
|
+
"native-uniwind": "React Native, Expo",
|
|
2704
|
+
"native-unistyles": "React Native, Expo"
|
|
2543
2705
|
};
|
|
2544
2706
|
for (const fe of frontend) if (frontendMap[fe]) {
|
|
2545
2707
|
parts.push(frontendMap[fe]);
|
|
@@ -2551,9 +2713,9 @@ function generateStackDescription(frontend, backend, api, isConvex) {
|
|
|
2551
2713
|
}
|
|
2552
2714
|
function generateRunningInstructions(frontend, backend, webPort, hasNative, isConvex) {
|
|
2553
2715
|
const instructions = [];
|
|
2554
|
-
const
|
|
2716
|
+
const hasAppWebFrontend = hasWebFrontend(frontend);
|
|
2555
2717
|
const isBackendSelf = backend === "self";
|
|
2556
|
-
if (
|
|
2718
|
+
if (hasAppWebFrontend) {
|
|
2557
2719
|
const desc = isBackendSelf ? "fullstack application" : "web application";
|
|
2558
2720
|
instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the ${desc}.`);
|
|
2559
2721
|
}
|
|
@@ -2596,7 +2758,7 @@ function generateProjectStructure(config) {
|
|
|
2596
2758
|
const { projectName, frontend, backend, addons, api, auth, database, orm } = config;
|
|
2597
2759
|
const isConvex = backend === "convex";
|
|
2598
2760
|
const structure = [`${projectName}/`, "├── apps/"];
|
|
2599
|
-
const
|
|
2761
|
+
const hasAppWebFrontend = hasWebFrontend(frontend);
|
|
2600
2762
|
const isBackendSelf = backend === "self";
|
|
2601
2763
|
const hasReactWeb = frontend.some((f) => [
|
|
2602
2764
|
"tanstack-router",
|
|
@@ -2604,13 +2766,9 @@ function generateProjectStructure(config) {
|
|
|
2604
2766
|
"tanstack-start",
|
|
2605
2767
|
"next"
|
|
2606
2768
|
].includes(f));
|
|
2607
|
-
const hasNative = frontend
|
|
2608
|
-
"native-bare",
|
|
2609
|
-
"native-uniwind",
|
|
2610
|
-
"native-unistyles"
|
|
2611
|
-
].includes(f));
|
|
2769
|
+
const hasNative = hasNativeFrontend(frontend);
|
|
2612
2770
|
const hasDbPackage = !isConvex && database !== "none" && orm !== "none";
|
|
2613
|
-
if (
|
|
2771
|
+
if (hasAppWebFrontend) {
|
|
2614
2772
|
const frontendTypes = {
|
|
2615
2773
|
"tanstack-router": "React + TanStack Router",
|
|
2616
2774
|
"react-router": "React + React Router",
|
|
@@ -2643,7 +2801,7 @@ function generateProjectStructure(config) {
|
|
|
2643
2801
|
}
|
|
2644
2802
|
if (!isConvex) {
|
|
2645
2803
|
if (api !== "none") structure.push("│ ├── api/ # API layer / business logic");
|
|
2646
|
-
if (auth
|
|
2804
|
+
if (auth === "better-auth") structure.push("│ ├── auth/ # Authentication configuration & logic");
|
|
2647
2805
|
if (hasDbPackage) structure.push("│ └── db/ # Database schema & queries");
|
|
2648
2806
|
}
|
|
2649
2807
|
}
|
|
@@ -2651,18 +2809,15 @@ function generateProjectStructure(config) {
|
|
|
2651
2809
|
}
|
|
2652
2810
|
function generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api) {
|
|
2653
2811
|
const isConvex = backend === "convex";
|
|
2654
|
-
const hasNative = frontend
|
|
2655
|
-
|
|
2656
|
-
"native-uniwind",
|
|
2657
|
-
"native-unistyles"
|
|
2658
|
-
].includes(f));
|
|
2659
|
-
const hasFrontend = frontend.length > 0 && !frontend.includes("none");
|
|
2812
|
+
const hasNative = hasNativeFrontend(frontend);
|
|
2813
|
+
const hasAppWebFrontend = hasWebFrontend(frontend);
|
|
2660
2814
|
const hasReactWeb = frontend.some((f) => [
|
|
2661
2815
|
"tanstack-router",
|
|
2662
2816
|
"react-router",
|
|
2663
2817
|
"tanstack-start",
|
|
2664
2818
|
"next"
|
|
2665
2819
|
].includes(f));
|
|
2820
|
+
const usesTailwind = hasAppWebFrontend || frontend.includes("native-uniwind");
|
|
2666
2821
|
const features = ["- **TypeScript** - For type safety and improved developer experience"];
|
|
2667
2822
|
const frontendFeatures = {
|
|
2668
2823
|
"tanstack-router": "- **TanStack Router** - File-based routing with full type safety",
|
|
@@ -2679,7 +2834,7 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
2679
2834
|
break;
|
|
2680
2835
|
}
|
|
2681
2836
|
if (hasNative) features.push("- **React Native** - Build mobile apps using React", "- **Expo** - Tools for React Native development");
|
|
2682
|
-
if (
|
|
2837
|
+
if (usesTailwind) features.push("- **TailwindCSS** - Utility-first CSS for rapid UI development");
|
|
2683
2838
|
if (hasReactWeb) features.push("- **Shared UI package** - shadcn/ui primitives live in `packages/ui`");
|
|
2684
2839
|
const backendFeatures = {
|
|
2685
2840
|
convex: "- **Convex** - Reactive backend-as-a-service platform",
|
|
@@ -3284,7 +3439,7 @@ async function processConfigPackage(vfs, templates, config) {
|
|
|
3284
3439
|
processTemplatesFromPrefix(vfs, templates, "packages/config", "packages/config", config);
|
|
3285
3440
|
}
|
|
3286
3441
|
async function processEnvPackage(vfs, templates, config) {
|
|
3287
|
-
const hasWebFrontend = config.frontend.some((f) => [
|
|
3442
|
+
const hasWebFrontend$1 = config.frontend.some((f) => [
|
|
3288
3443
|
"tanstack-router",
|
|
3289
3444
|
"react-router",
|
|
3290
3445
|
"tanstack-start",
|
|
@@ -3299,10 +3454,10 @@ async function processEnvPackage(vfs, templates, config) {
|
|
|
3299
3454
|
"native-uniwind",
|
|
3300
3455
|
"native-unistyles"
|
|
3301
3456
|
].includes(f));
|
|
3302
|
-
if (!hasWebFrontend && !hasNative && config.backend === "none") return;
|
|
3457
|
+
if (!hasWebFrontend$1 && !hasNative && config.backend === "none") return;
|
|
3303
3458
|
processSingleTemplate(vfs, templates, "packages/env/package.json", "packages/env/package.json", config);
|
|
3304
3459
|
processSingleTemplate(vfs, templates, "packages/env/tsconfig.json", "packages/env/tsconfig.json", config);
|
|
3305
|
-
if (hasWebFrontend) processSingleTemplate(vfs, templates, "packages/env/src/web.ts", "packages/env/src/web.ts", config);
|
|
3460
|
+
if (hasWebFrontend$1) processSingleTemplate(vfs, templates, "packages/env/src/web.ts", "packages/env/src/web.ts", config);
|
|
3306
3461
|
if (hasNative) processSingleTemplate(vfs, templates, "packages/env/src/native.ts", "packages/env/src/native.ts", config);
|
|
3307
3462
|
if (config.backend !== "none" && config.backend !== "convex") processSingleTemplate(vfs, templates, "packages/env/src/server.ts", "packages/env/src/server.ts", config);
|
|
3308
3463
|
}
|
|
@@ -4219,6 +4374,8 @@ import { env } from "@{{projectName}}/env/native";
|
|
|
4219
4374
|
{{#if (eq auth "better-auth")}}
|
|
4220
4375
|
import { authClient } from "@/lib/auth-client";
|
|
4221
4376
|
import { Platform } from "react-native";
|
|
4377
|
+
{{else if (eq auth "clerk")}}
|
|
4378
|
+
import { getClerkAuthToken } from "@/utils/clerk-auth";
|
|
4222
4379
|
{{/if}}
|
|
4223
4380
|
|
|
4224
4381
|
export const queryClient = new QueryClient({
|
|
@@ -4259,6 +4416,11 @@ export const link = new RPCLink({
|
|
|
4259
4416
|
}
|
|
4260
4417
|
return Object.fromEntries(headers);
|
|
4261
4418
|
},
|
|
4419
|
+
{{else if (eq auth "clerk")}}
|
|
4420
|
+
headers: async () => {
|
|
4421
|
+
const token = await getClerkAuthToken();
|
|
4422
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
4423
|
+
},
|
|
4262
4424
|
{{/if}}
|
|
4263
4425
|
});
|
|
4264
4426
|
|
|
@@ -4316,22 +4478,64 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|
|
4316
4478
|
"devDependencies": {},
|
|
4317
4479
|
"dependencies": {}
|
|
4318
4480
|
}`],
|
|
4319
|
-
["api/orpc/server/src/context.ts.hbs", `{{#if (
|
|
4481
|
+
["api/orpc/server/src/context.ts.hbs", `{{#if (eq auth "clerk")}}
|
|
4482
|
+
type ClerkContextAuth = {
|
|
4483
|
+
userId: string | null;
|
|
4484
|
+
};
|
|
4485
|
+
|
|
4486
|
+
type ClerkRequestContext = {
|
|
4487
|
+
auth: ClerkContextAuth | null;
|
|
4488
|
+
session: null;
|
|
4489
|
+
};
|
|
4490
|
+
|
|
4491
|
+
function toClerkContextAuth(auth: { userId: string | null } | null): ClerkContextAuth | null {
|
|
4492
|
+
return auth ? { userId: auth.userId } : null;
|
|
4493
|
+
}
|
|
4494
|
+
{{/if}}
|
|
4495
|
+
|
|
4496
|
+
{{#if (and (eq auth "clerk") (or (eq backend 'self') (eq backend 'hono') (eq backend 'elysia')))}}
|
|
4497
|
+
import { createClerkClient } from "@clerk/backend";
|
|
4498
|
+
import { env } from "@{{projectName}}/env/server";
|
|
4499
|
+
|
|
4500
|
+
const clerkClient = createClerkClient({
|
|
4501
|
+
secretKey: env.CLERK_SECRET_KEY,
|
|
4502
|
+
publishableKey: env.CLERK_PUBLISHABLE_KEY,
|
|
4503
|
+
});
|
|
4504
|
+
|
|
4505
|
+
async function authenticateClerkRequest(request: Request): Promise<ClerkContextAuth | null> {
|
|
4506
|
+
const requestState = await clerkClient.authenticateRequest(request, {
|
|
4507
|
+
authorizedParties: [env.CORS_ORIGIN],
|
|
4508
|
+
});
|
|
4509
|
+
return toClerkContextAuth(requestState.toAuth());
|
|
4510
|
+
}
|
|
4511
|
+
{{/if}}
|
|
4512
|
+
|
|
4513
|
+
{{#if (and (eq backend 'self') (includes frontend "next"))}}
|
|
4320
4514
|
import type { NextRequest } from "next/server";
|
|
4321
4515
|
{{#if (eq auth "better-auth")}}
|
|
4322
4516
|
import { auth } from "@{{projectName}}/auth";
|
|
4323
4517
|
{{/if}}
|
|
4324
4518
|
|
|
4325
|
-
export async function createContext(req: NextRequest) {
|
|
4519
|
+
export async function createContext(req: NextRequest){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
4326
4520
|
{{#if (eq auth "better-auth")}}
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4521
|
+
const session = await auth.api.getSession({
|
|
4522
|
+
headers: req.headers,
|
|
4523
|
+
});
|
|
4524
|
+
return {
|
|
4525
|
+
auth: null,
|
|
4526
|
+
session,
|
|
4527
|
+
};
|
|
4528
|
+
{{else if (eq auth "clerk")}}
|
|
4529
|
+
const clerkAuth = await authenticateClerkRequest(req);
|
|
4530
|
+
return {
|
|
4531
|
+
auth: clerkAuth,
|
|
4532
|
+
session: null,
|
|
4533
|
+
};
|
|
4333
4534
|
{{else}}
|
|
4334
|
-
|
|
4535
|
+
return {
|
|
4536
|
+
auth: null,
|
|
4537
|
+
session: null,
|
|
4538
|
+
};
|
|
4335
4539
|
{{/if}}
|
|
4336
4540
|
}
|
|
4337
4541
|
|
|
@@ -4340,16 +4544,26 @@ export async function createContext(req: NextRequest) {
|
|
|
4340
4544
|
import { auth } from "@{{projectName}}/auth";
|
|
4341
4545
|
{{/if}}
|
|
4342
4546
|
|
|
4343
|
-
export async function createContext({ req }: { req: Request }) {
|
|
4547
|
+
export async function createContext({ req }: { req: Request }){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
4344
4548
|
{{#if (eq auth "better-auth")}}
|
|
4345
4549
|
const session = await auth.api.getSession({
|
|
4346
4550
|
headers: req.headers,
|
|
4347
4551
|
});
|
|
4348
4552
|
return {
|
|
4553
|
+
auth: null,
|
|
4349
4554
|
session,
|
|
4350
4555
|
};
|
|
4556
|
+
{{else if (eq auth "clerk")}}
|
|
4557
|
+
const clerkAuth = await authenticateClerkRequest(req);
|
|
4558
|
+
return {
|
|
4559
|
+
auth: clerkAuth,
|
|
4560
|
+
session: null,
|
|
4561
|
+
};
|
|
4351
4562
|
{{else}}
|
|
4352
|
-
return {
|
|
4563
|
+
return {
|
|
4564
|
+
auth: null,
|
|
4565
|
+
session: null,
|
|
4566
|
+
};
|
|
4353
4567
|
{{/if}}
|
|
4354
4568
|
}
|
|
4355
4569
|
|
|
@@ -4359,17 +4573,21 @@ import { auth } from "@{{projectName}}/auth";
|
|
|
4359
4573
|
{{/if}}
|
|
4360
4574
|
|
|
4361
4575
|
export type CreateContextOptions = {
|
|
4362
|
-
|
|
4576
|
+
headers: Headers;
|
|
4363
4577
|
};
|
|
4364
4578
|
|
|
4365
4579
|
export async function createContext({ headers }: CreateContextOptions) {
|
|
4366
4580
|
{{#if (eq auth "better-auth")}}
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4581
|
+
const session = await auth.api.getSession({ headers });
|
|
4582
|
+
return {
|
|
4583
|
+
auth: null,
|
|
4584
|
+
session,
|
|
4585
|
+
};
|
|
4371
4586
|
{{else}}
|
|
4372
|
-
|
|
4587
|
+
return {
|
|
4588
|
+
auth: null,
|
|
4589
|
+
session: null,
|
|
4590
|
+
};
|
|
4373
4591
|
{{/if}}
|
|
4374
4592
|
}
|
|
4375
4593
|
|
|
@@ -4379,17 +4597,21 @@ import { auth } from "@{{projectName}}/auth";
|
|
|
4379
4597
|
{{/if}}
|
|
4380
4598
|
|
|
4381
4599
|
export type CreateContextOptions = {
|
|
4382
|
-
|
|
4600
|
+
headers: Headers;
|
|
4383
4601
|
};
|
|
4384
4602
|
|
|
4385
4603
|
export async function createContext({ headers }: CreateContextOptions) {
|
|
4386
4604
|
{{#if (eq auth "better-auth")}}
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4605
|
+
const session = await auth.api.getSession({ headers });
|
|
4606
|
+
return {
|
|
4607
|
+
auth: null,
|
|
4608
|
+
session,
|
|
4609
|
+
};
|
|
4391
4610
|
{{else}}
|
|
4392
|
-
|
|
4611
|
+
return {
|
|
4612
|
+
auth: null,
|
|
4613
|
+
session: null,
|
|
4614
|
+
};
|
|
4393
4615
|
{{/if}}
|
|
4394
4616
|
}
|
|
4395
4617
|
|
|
@@ -4400,22 +4622,29 @@ import { auth } from "@{{projectName}}/auth";
|
|
|
4400
4622
|
{{/if}}
|
|
4401
4623
|
|
|
4402
4624
|
export type CreateContextOptions = {
|
|
4403
|
-
|
|
4625
|
+
context: HonoContext;
|
|
4404
4626
|
};
|
|
4405
4627
|
|
|
4406
|
-
export async function createContext({ context }: CreateContextOptions) {
|
|
4628
|
+
export async function createContext({ context }: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
4407
4629
|
{{#if (eq auth "better-auth")}}
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4630
|
+
const session = await auth.api.getSession({
|
|
4631
|
+
headers: context.req.raw.headers,
|
|
4632
|
+
});
|
|
4633
|
+
return {
|
|
4634
|
+
auth: null,
|
|
4635
|
+
session,
|
|
4636
|
+
};
|
|
4637
|
+
{{else if (eq auth "clerk")}}
|
|
4638
|
+
const clerkAuth = await authenticateClerkRequest(context.req.raw);
|
|
4639
|
+
return {
|
|
4640
|
+
auth: clerkAuth,
|
|
4641
|
+
session: null,
|
|
4642
|
+
};
|
|
4414
4643
|
{{else}}
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4644
|
+
return {
|
|
4645
|
+
auth: null,
|
|
4646
|
+
session: null,
|
|
4647
|
+
};
|
|
4419
4648
|
{{/if}}
|
|
4420
4649
|
}
|
|
4421
4650
|
|
|
@@ -4426,22 +4655,29 @@ import { auth } from "@{{projectName}}/auth";
|
|
|
4426
4655
|
{{/if}}
|
|
4427
4656
|
|
|
4428
4657
|
export type CreateContextOptions = {
|
|
4429
|
-
|
|
4658
|
+
context: ElysiaContext;
|
|
4430
4659
|
};
|
|
4431
4660
|
|
|
4432
|
-
export async function createContext({ context }: CreateContextOptions) {
|
|
4661
|
+
export async function createContext({ context }: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
4433
4662
|
{{#if (eq auth "better-auth")}}
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4663
|
+
const session = await auth.api.getSession({
|
|
4664
|
+
headers: context.request.headers,
|
|
4665
|
+
});
|
|
4666
|
+
return {
|
|
4667
|
+
auth: null,
|
|
4668
|
+
session,
|
|
4669
|
+
};
|
|
4670
|
+
{{else if (eq auth "clerk")}}
|
|
4671
|
+
const clerkAuth = await authenticateClerkRequest(context.request);
|
|
4672
|
+
return {
|
|
4673
|
+
auth: clerkAuth,
|
|
4674
|
+
session: null,
|
|
4675
|
+
};
|
|
4440
4676
|
{{else}}
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4677
|
+
return {
|
|
4678
|
+
auth: null,
|
|
4679
|
+
session: null,
|
|
4680
|
+
};
|
|
4445
4681
|
{{/if}}
|
|
4446
4682
|
}
|
|
4447
4683
|
|
|
@@ -4450,56 +4686,77 @@ import type { Request } from "express";
|
|
|
4450
4686
|
{{#if (eq auth "better-auth")}}
|
|
4451
4687
|
import { fromNodeHeaders } from "better-auth/node";
|
|
4452
4688
|
import { auth } from "@{{projectName}}/auth";
|
|
4689
|
+
{{else if (eq auth "clerk")}}
|
|
4690
|
+
import { getAuth } from "@clerk/express";
|
|
4453
4691
|
{{/if}}
|
|
4454
4692
|
|
|
4455
4693
|
interface CreateContextOptions {
|
|
4456
4694
|
req: Request;
|
|
4457
4695
|
}
|
|
4458
4696
|
|
|
4459
|
-
export async function createContext(opts: CreateContextOptions) {
|
|
4697
|
+
export async function createContext(opts: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
4460
4698
|
{{#if (eq auth "better-auth")}}
|
|
4461
4699
|
const session = await auth.api.getSession({
|
|
4462
4700
|
headers: fromNodeHeaders(opts.req.headers),
|
|
4463
4701
|
});
|
|
4464
4702
|
return {
|
|
4703
|
+
auth: null,
|
|
4465
4704
|
session,
|
|
4466
4705
|
};
|
|
4706
|
+
{{else if (eq auth "clerk")}}
|
|
4707
|
+
const clerkAuth = toClerkContextAuth(getAuth(opts.req));
|
|
4708
|
+
return {
|
|
4709
|
+
auth: clerkAuth,
|
|
4710
|
+
session: null,
|
|
4711
|
+
};
|
|
4467
4712
|
{{else}}
|
|
4468
|
-
// No auth configured
|
|
4469
4713
|
return {
|
|
4714
|
+
auth: null,
|
|
4470
4715
|
session: null,
|
|
4471
4716
|
};
|
|
4472
4717
|
{{/if}}
|
|
4473
4718
|
}
|
|
4474
4719
|
|
|
4475
4720
|
{{else if (eq backend 'fastify')}}
|
|
4476
|
-
import type { IncomingHttpHeaders } from "node:http";
|
|
4477
4721
|
{{#if (eq auth "better-auth")}}
|
|
4722
|
+
import type { IncomingHttpHeaders } from "node:http";
|
|
4478
4723
|
import { fromNodeHeaders } from "better-auth/node";
|
|
4479
4724
|
import { auth } from "@{{projectName}}/auth";
|
|
4725
|
+
{{else if (eq auth "clerk")}}
|
|
4726
|
+
import { getAuth } from "@clerk/fastify";
|
|
4727
|
+
{{else}}
|
|
4728
|
+
import type { IncomingHttpHeaders } from "node:http";
|
|
4480
4729
|
{{/if}}
|
|
4481
4730
|
|
|
4482
|
-
export async function createContext(req: IncomingHttpHeaders) {
|
|
4731
|
+
export async function createContext(req: {{#if (eq auth "clerk")}}Parameters<typeof getAuth>[0]{{else}}IncomingHttpHeaders{{/if}}){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
4483
4732
|
{{#if (eq auth "better-auth")}}
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4733
|
+
const session = await auth.api.getSession({
|
|
4734
|
+
headers: fromNodeHeaders(req),
|
|
4735
|
+
});
|
|
4736
|
+
return {
|
|
4737
|
+
auth: null,
|
|
4738
|
+
session,
|
|
4739
|
+
};
|
|
4740
|
+
{{else if (eq auth "clerk")}}
|
|
4741
|
+
const clerkAuth = toClerkContextAuth(getAuth(req));
|
|
4742
|
+
return {
|
|
4743
|
+
auth: clerkAuth,
|
|
4744
|
+
session: null,
|
|
4745
|
+
};
|
|
4490
4746
|
{{else}}
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4747
|
+
return {
|
|
4748
|
+
auth: null,
|
|
4749
|
+
session: null,
|
|
4750
|
+
};
|
|
4495
4751
|
{{/if}}
|
|
4496
4752
|
}
|
|
4497
4753
|
|
|
4498
4754
|
{{else}}
|
|
4499
4755
|
export async function createContext() {
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4756
|
+
return {
|
|
4757
|
+
auth: null,
|
|
4758
|
+
session: null,
|
|
4759
|
+
};
|
|
4503
4760
|
}
|
|
4504
4761
|
{{/if}}
|
|
4505
4762
|
|
|
@@ -4512,8 +4769,9 @@ export const o = os.$context<Context>();
|
|
|
4512
4769
|
|
|
4513
4770
|
export const publicProcedure = o;
|
|
4514
4771
|
|
|
4515
|
-
{{#if (eq auth "better-auth")}}
|
|
4772
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
4516
4773
|
const requireAuth = o.middleware(async ({ context, next }) => {
|
|
4774
|
+
{{#if (eq auth "better-auth")}}
|
|
4517
4775
|
if (!context.session?.user) {
|
|
4518
4776
|
throw new ORPCError("UNAUTHORIZED");
|
|
4519
4777
|
}
|
|
@@ -4522,13 +4780,23 @@ const requireAuth = o.middleware(async ({ context, next }) => {
|
|
|
4522
4780
|
session: context.session,
|
|
4523
4781
|
},
|
|
4524
4782
|
});
|
|
4783
|
+
{{else}}
|
|
4784
|
+
if (!context.auth?.userId) {
|
|
4785
|
+
throw new ORPCError("UNAUTHORIZED");
|
|
4786
|
+
}
|
|
4787
|
+
return next({
|
|
4788
|
+
context: {
|
|
4789
|
+
auth: context.auth,
|
|
4790
|
+
},
|
|
4791
|
+
});
|
|
4792
|
+
{{/if}}
|
|
4525
4793
|
});
|
|
4526
4794
|
|
|
4527
4795
|
export const protectedProcedure = publicProcedure.use(requireAuth);
|
|
4528
4796
|
{{/if}}
|
|
4529
4797
|
`],
|
|
4530
4798
|
["api/orpc/server/src/routers/index.ts.hbs", `{{#if (eq api "orpc")}}
|
|
4531
|
-
import { {{#if (eq auth "better-auth")}}protectedProcedure, {{/if}}publicProcedure } from "../index";
|
|
4799
|
+
import { {{#if (or (eq auth "better-auth") (eq auth "clerk"))}}protectedProcedure, {{/if}}publicProcedure } from "../index";
|
|
4532
4800
|
import type { RouterClient } from "@orpc/server";
|
|
4533
4801
|
{{#if (includes examples "todo")}}
|
|
4534
4802
|
import { todoRouter } from "./todo";
|
|
@@ -4538,11 +4806,15 @@ export const appRouter = {
|
|
|
4538
4806
|
healthCheck: publicProcedure.handler(() => {
|
|
4539
4807
|
return "OK";
|
|
4540
4808
|
}),
|
|
4541
|
-
{{#if (eq auth "better-auth")}}
|
|
4809
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
4542
4810
|
privateData: protectedProcedure.handler(({ context }) => {
|
|
4543
4811
|
return {
|
|
4544
4812
|
message: "This is private",
|
|
4813
|
+
{{#if (eq auth "better-auth")}}
|
|
4545
4814
|
user: context.session?.user,
|
|
4815
|
+
{{else}}
|
|
4816
|
+
userId: context.auth?.userId,
|
|
4817
|
+
{{/if}}
|
|
4546
4818
|
};
|
|
4547
4819
|
}),
|
|
4548
4820
|
{{/if}}
|
|
@@ -4719,11 +4991,17 @@ import { createContext } from "@{{projectName}}/api/context";
|
|
|
4719
4991
|
import type { RouterClient } from "@orpc/server";
|
|
4720
4992
|
import type { AppRouter } from "@{{projectName}}/api/routers/index";
|
|
4721
4993
|
import { env } from "@{{projectName}}/env/web";
|
|
4994
|
+
{{#if (eq auth "clerk")}}
|
|
4995
|
+
import { getClerkAuthToken } from "@/utils/clerk-auth";
|
|
4996
|
+
{{/if}}
|
|
4722
4997
|
{{else}}
|
|
4723
4998
|
import type { AppRouterClient } from "@{{projectName}}/api/routers/index";
|
|
4724
4999
|
{{#unless (eq backend "self")}}
|
|
4725
5000
|
import { env } from "@{{projectName}}/env/web";
|
|
4726
5001
|
{{/unless}}
|
|
5002
|
+
{{#if (eq auth "clerk")}}
|
|
5003
|
+
import { getClerkAuthToken } from "@/utils/clerk-auth";
|
|
5004
|
+
{{/if}}
|
|
4727
5005
|
{{/if}}
|
|
4728
5006
|
|
|
4729
5007
|
export const queryClient = new QueryClient({
|
|
@@ -4768,6 +5046,12 @@ export const client: RouterClient<typeof appRouter> = getORPCClient();
|
|
|
4768
5046
|
{{else if (includes frontend "tanstack-start")}}
|
|
4769
5047
|
const link = new RPCLink({
|
|
4770
5048
|
url: \`\${env.VITE_SERVER_URL}/rpc\`,
|
|
5049
|
+
{{#if (eq auth "clerk")}}
|
|
5050
|
+
headers: async () => {
|
|
5051
|
+
const token = await getClerkAuthToken();
|
|
5052
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
5053
|
+
},
|
|
5054
|
+
{{/if}}
|
|
4771
5055
|
{{#if (eq auth "better-auth")}}
|
|
4772
5056
|
fetch(url, options) {
|
|
4773
5057
|
return fetch(url, {
|
|
@@ -4792,6 +5076,25 @@ export const link = new RPCLink({
|
|
|
4792
5076
|
{{else}}
|
|
4793
5077
|
url: \`\${env.VITE_SERVER_URL}/rpc\`,
|
|
4794
5078
|
{{/if}}
|
|
5079
|
+
{{#if (eq auth "clerk")}}
|
|
5080
|
+
headers: async () => {
|
|
5081
|
+
{{#if (includes frontend "next")}}
|
|
5082
|
+
if (typeof window !== "undefined") {
|
|
5083
|
+
const token = await getClerkAuthToken();
|
|
5084
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
5085
|
+
}
|
|
5086
|
+
|
|
5087
|
+
const { auth } = await import("@clerk/nextjs/server");
|
|
5088
|
+
const clerkAuth = await auth();
|
|
5089
|
+
const token = await clerkAuth.getToken();
|
|
5090
|
+
|
|
5091
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
5092
|
+
{{else}}
|
|
5093
|
+
const token = await getClerkAuthToken();
|
|
5094
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
5095
|
+
{{/if}}
|
|
5096
|
+
},
|
|
5097
|
+
{{/if}}
|
|
4795
5098
|
{{#if (eq auth "better-auth")}}
|
|
4796
5099
|
fetch(url, options) {
|
|
4797
5100
|
return fetch(url, {
|
|
@@ -4816,7 +5119,6 @@ export const client: AppRouterClient = createORPCClient(link)
|
|
|
4816
5119
|
{{/if}}
|
|
4817
5120
|
|
|
4818
5121
|
export const orpc = createTanstackQueryUtils(client)
|
|
4819
|
-
|
|
4820
5122
|
`],
|
|
4821
5123
|
["api/orpc/web/solid/src/utils/orpc.ts.hbs", `import { createORPCClient } from "@orpc/client";
|
|
4822
5124
|
import { RPCLink } from "@orpc/client/fetch";
|
|
@@ -4921,6 +5223,8 @@ export const Route = createFileRoute('/api/trpc/$')({
|
|
|
4921
5223
|
["api/trpc/native/utils/trpc.ts.hbs", `{{#if (eq auth "better-auth")}}
|
|
4922
5224
|
import { authClient } from "@/lib/auth-client";
|
|
4923
5225
|
import { Platform } from "react-native";
|
|
5226
|
+
{{else if (eq auth "clerk")}}
|
|
5227
|
+
import { getClerkAuthToken } from "@/utils/clerk-auth";
|
|
4924
5228
|
{{/if}}
|
|
4925
5229
|
import { QueryClient } from "@tanstack/react-query";
|
|
4926
5230
|
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
|
@@ -4958,6 +5262,11 @@ const trpcClient = createTRPCClient<AppRouter>({
|
|
|
4958
5262
|
}
|
|
4959
5263
|
return Object.fromEntries(headers);
|
|
4960
5264
|
},
|
|
5265
|
+
{{else if (eq auth "clerk")}}
|
|
5266
|
+
headers: async function () {
|
|
5267
|
+
const token = await getClerkAuthToken();
|
|
5268
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
5269
|
+
},
|
|
4961
5270
|
{{/if}}
|
|
4962
5271
|
}),
|
|
4963
5272
|
],
|
|
@@ -5017,25 +5326,64 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|
|
5017
5326
|
"scripts": {},
|
|
5018
5327
|
"devDependencies": {}
|
|
5019
5328
|
}`],
|
|
5020
|
-
["api/trpc/server/src/context.ts.hbs", `{{#if (
|
|
5021
|
-
|
|
5329
|
+
["api/trpc/server/src/context.ts.hbs", `{{#if (eq auth "clerk")}}
|
|
5330
|
+
type ClerkContextAuth = {
|
|
5331
|
+
userId: string | null;
|
|
5332
|
+
};
|
|
5333
|
+
|
|
5334
|
+
type ClerkRequestContext = {
|
|
5335
|
+
auth: ClerkContextAuth | null;
|
|
5336
|
+
session: null;
|
|
5337
|
+
};
|
|
5338
|
+
|
|
5339
|
+
function toClerkContextAuth(auth: { userId: string | null } | null): ClerkContextAuth | null {
|
|
5340
|
+
return auth ? { userId: auth.userId } : null;
|
|
5341
|
+
}
|
|
5342
|
+
{{/if}}
|
|
5343
|
+
|
|
5344
|
+
{{#if (and (eq auth "clerk") (or (eq backend 'self') (eq backend 'hono') (eq backend 'elysia')))}}
|
|
5345
|
+
import { createClerkClient } from "@clerk/backend";
|
|
5346
|
+
import { env } from "@{{projectName}}/env/server";
|
|
5347
|
+
|
|
5348
|
+
const clerkClient = createClerkClient({
|
|
5349
|
+
secretKey: env.CLERK_SECRET_KEY,
|
|
5350
|
+
publishableKey: env.CLERK_PUBLISHABLE_KEY,
|
|
5351
|
+
});
|
|
5352
|
+
|
|
5353
|
+
async function authenticateClerkRequest(request: Request): Promise<ClerkContextAuth | null> {
|
|
5354
|
+
const requestState = await clerkClient.authenticateRequest(request, {
|
|
5355
|
+
authorizedParties: [env.CORS_ORIGIN],
|
|
5356
|
+
});
|
|
5357
|
+
return toClerkContextAuth(requestState.toAuth());
|
|
5358
|
+
}
|
|
5359
|
+
{{/if}}
|
|
5360
|
+
|
|
5361
|
+
{{#if (and (eq backend 'self') (includes frontend "next"))}}
|
|
5362
|
+
import type { NextRequest } from "next/server";
|
|
5022
5363
|
{{#if (eq auth "better-auth")}}
|
|
5023
5364
|
import { auth } from "@{{projectName}}/auth";
|
|
5024
5365
|
{{/if}}
|
|
5025
5366
|
|
|
5026
|
-
export async function createContext(req: NextRequest) {
|
|
5367
|
+
export async function createContext(req: NextRequest){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
5027
5368
|
{{#if (eq auth "better-auth")}}
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5369
|
+
const session = await auth.api.getSession({
|
|
5370
|
+
headers: req.headers,
|
|
5371
|
+
});
|
|
5372
|
+
return {
|
|
5373
|
+
auth: null,
|
|
5374
|
+
session,
|
|
5375
|
+
};
|
|
5376
|
+
{{else if (eq auth "clerk")}}
|
|
5377
|
+
const clerkAuth = await authenticateClerkRequest(req);
|
|
5378
|
+
return {
|
|
5379
|
+
auth: clerkAuth,
|
|
5380
|
+
session: null,
|
|
5381
|
+
};
|
|
5034
5382
|
{{else}}
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5383
|
+
return {
|
|
5384
|
+
auth: null,
|
|
5385
|
+
session: null,
|
|
5386
|
+
};
|
|
5039
5387
|
{{/if}}
|
|
5040
5388
|
}
|
|
5041
5389
|
|
|
@@ -5044,17 +5392,24 @@ export async function createContext(req: NextRequest) {
|
|
|
5044
5392
|
import { auth } from "@{{projectName}}/auth";
|
|
5045
5393
|
{{/if}}
|
|
5046
5394
|
|
|
5047
|
-
export async function createContext({ req }: { req: Request }) {
|
|
5395
|
+
export async function createContext({ req }: { req: Request }){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
5048
5396
|
{{#if (eq auth "better-auth")}}
|
|
5049
5397
|
const session = await auth.api.getSession({
|
|
5050
5398
|
headers: req.headers,
|
|
5051
5399
|
});
|
|
5052
5400
|
return {
|
|
5401
|
+
auth: null,
|
|
5053
5402
|
session,
|
|
5054
5403
|
};
|
|
5404
|
+
{{else if (eq auth "clerk")}}
|
|
5405
|
+
const clerkAuth = await authenticateClerkRequest(req);
|
|
5406
|
+
return {
|
|
5407
|
+
auth: clerkAuth,
|
|
5408
|
+
session: null,
|
|
5409
|
+
};
|
|
5055
5410
|
{{else}}
|
|
5056
|
-
// No auth configured
|
|
5057
5411
|
return {
|
|
5412
|
+
auth: null,
|
|
5058
5413
|
session: null,
|
|
5059
5414
|
};
|
|
5060
5415
|
{{/if}}
|
|
@@ -5067,22 +5422,29 @@ import { auth } from "@{{projectName}}/auth";
|
|
|
5067
5422
|
{{/if}}
|
|
5068
5423
|
|
|
5069
5424
|
export type CreateContextOptions = {
|
|
5070
|
-
|
|
5425
|
+
context: HonoContext;
|
|
5071
5426
|
};
|
|
5072
5427
|
|
|
5073
|
-
export async function createContext({ context }: CreateContextOptions) {
|
|
5428
|
+
export async function createContext({ context }: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
5074
5429
|
{{#if (eq auth "better-auth")}}
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5430
|
+
const session = await auth.api.getSession({
|
|
5431
|
+
headers: context.req.raw.headers,
|
|
5432
|
+
});
|
|
5433
|
+
return {
|
|
5434
|
+
auth: null,
|
|
5435
|
+
session,
|
|
5436
|
+
};
|
|
5437
|
+
{{else if (eq auth "clerk")}}
|
|
5438
|
+
const clerkAuth = await authenticateClerkRequest(context.req.raw);
|
|
5439
|
+
return {
|
|
5440
|
+
auth: clerkAuth,
|
|
5441
|
+
session: null,
|
|
5442
|
+
};
|
|
5081
5443
|
{{else}}
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5444
|
+
return {
|
|
5445
|
+
auth: null,
|
|
5446
|
+
session: null,
|
|
5447
|
+
};
|
|
5086
5448
|
{{/if}}
|
|
5087
5449
|
}
|
|
5088
5450
|
|
|
@@ -5093,22 +5455,29 @@ import { auth } from "@{{projectName}}/auth";
|
|
|
5093
5455
|
{{/if}}
|
|
5094
5456
|
|
|
5095
5457
|
export type CreateContextOptions = {
|
|
5096
|
-
|
|
5458
|
+
context: ElysiaContext;
|
|
5097
5459
|
};
|
|
5098
5460
|
|
|
5099
|
-
export async function createContext({ context }: CreateContextOptions) {
|
|
5461
|
+
export async function createContext({ context }: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
5100
5462
|
{{#if (eq auth "better-auth")}}
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5463
|
+
const session = await auth.api.getSession({
|
|
5464
|
+
headers: context.request.headers,
|
|
5465
|
+
});
|
|
5466
|
+
return {
|
|
5467
|
+
auth: null,
|
|
5468
|
+
session,
|
|
5469
|
+
};
|
|
5470
|
+
{{else if (eq auth "clerk")}}
|
|
5471
|
+
const clerkAuth = await authenticateClerkRequest(context.request);
|
|
5472
|
+
return {
|
|
5473
|
+
auth: clerkAuth,
|
|
5474
|
+
session: null,
|
|
5475
|
+
};
|
|
5107
5476
|
{{else}}
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5477
|
+
return {
|
|
5478
|
+
auth: null,
|
|
5479
|
+
session: null,
|
|
5480
|
+
};
|
|
5112
5481
|
{{/if}}
|
|
5113
5482
|
}
|
|
5114
5483
|
|
|
@@ -5117,19 +5486,28 @@ import type { CreateExpressContextOptions } from "@trpc/server/adapters/express"
|
|
|
5117
5486
|
{{#if (eq auth "better-auth")}}
|
|
5118
5487
|
import { fromNodeHeaders } from "better-auth/node";
|
|
5119
5488
|
import { auth } from "@{{projectName}}/auth";
|
|
5489
|
+
{{else if (eq auth "clerk")}}
|
|
5490
|
+
import { getAuth } from "@clerk/express";
|
|
5120
5491
|
{{/if}}
|
|
5121
5492
|
|
|
5122
|
-
export async function createContext(opts: CreateExpressContextOptions) {
|
|
5493
|
+
export async function createContext(opts: CreateExpressContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
5123
5494
|
{{#if (eq auth "better-auth")}}
|
|
5124
5495
|
const session = await auth.api.getSession({
|
|
5125
5496
|
headers: fromNodeHeaders(opts.req.headers),
|
|
5126
5497
|
});
|
|
5127
5498
|
return {
|
|
5499
|
+
auth: null,
|
|
5128
5500
|
session,
|
|
5129
5501
|
};
|
|
5502
|
+
{{else if (eq auth "clerk")}}
|
|
5503
|
+
const clerkAuth = toClerkContextAuth(getAuth(opts.req));
|
|
5504
|
+
return {
|
|
5505
|
+
auth: clerkAuth,
|
|
5506
|
+
session: null,
|
|
5507
|
+
};
|
|
5130
5508
|
{{else}}
|
|
5131
|
-
// No auth configured
|
|
5132
5509
|
return {
|
|
5510
|
+
auth: null,
|
|
5133
5511
|
session: null,
|
|
5134
5512
|
};
|
|
5135
5513
|
{{/if}}
|
|
@@ -5140,17 +5518,28 @@ import type { CreateFastifyContextOptions } from "@trpc/server/adapters/fastify"
|
|
|
5140
5518
|
{{#if (eq auth "better-auth")}}
|
|
5141
5519
|
import { fromNodeHeaders } from "better-auth/node";
|
|
5142
5520
|
import { auth } from "@{{projectName}}/auth";
|
|
5521
|
+
{{else if (eq auth "clerk")}}
|
|
5522
|
+
import { getAuth } from "@clerk/fastify";
|
|
5143
5523
|
{{/if}}
|
|
5144
5524
|
|
|
5145
|
-
export async function createContext({ req
|
|
5525
|
+
export async function createContext({ req }: CreateFastifyContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
|
|
5146
5526
|
{{#if (eq auth "better-auth")}}
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5527
|
+
const session = await auth.api.getSession({
|
|
5528
|
+
headers: fromNodeHeaders(req.headers),
|
|
5529
|
+
});
|
|
5530
|
+
return {
|
|
5531
|
+
auth: null,
|
|
5532
|
+
session,
|
|
5533
|
+
};
|
|
5534
|
+
{{else if (eq auth "clerk")}}
|
|
5535
|
+
const clerkAuth = toClerkContextAuth(getAuth(req));
|
|
5536
|
+
return {
|
|
5537
|
+
auth: clerkAuth,
|
|
5538
|
+
session: null,
|
|
5539
|
+
};
|
|
5151
5540
|
{{else}}
|
|
5152
|
-
// No auth configured
|
|
5153
5541
|
return {
|
|
5542
|
+
auth: null,
|
|
5154
5543
|
session: null,
|
|
5155
5544
|
};
|
|
5156
5545
|
{{/if}}
|
|
@@ -5158,9 +5547,10 @@ export async function createContext({ req, res }: CreateFastifyContextOptions) {
|
|
|
5158
5547
|
|
|
5159
5548
|
{{else}}
|
|
5160
5549
|
export async function createContext() {
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5550
|
+
return {
|
|
5551
|
+
auth: null,
|
|
5552
|
+
session: null,
|
|
5553
|
+
};
|
|
5164
5554
|
}
|
|
5165
5555
|
{{/if}}
|
|
5166
5556
|
|
|
@@ -5175,8 +5565,9 @@ export const router = t.router;
|
|
|
5175
5565
|
|
|
5176
5566
|
export const publicProcedure = t.procedure;
|
|
5177
5567
|
|
|
5178
|
-
{{#if (eq auth "better-auth")}}
|
|
5568
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
5179
5569
|
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
|
|
5570
|
+
{{#if (eq auth "better-auth")}}
|
|
5180
5571
|
if (!ctx.session) {
|
|
5181
5572
|
throw new TRPCError({
|
|
5182
5573
|
code: "UNAUTHORIZED",
|
|
@@ -5184,10 +5575,23 @@ export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
|
|
|
5184
5575
|
cause: "No session",
|
|
5185
5576
|
});
|
|
5186
5577
|
}
|
|
5578
|
+
{{else}}
|
|
5579
|
+
if (!ctx.auth?.userId) {
|
|
5580
|
+
throw new TRPCError({
|
|
5581
|
+
code: "UNAUTHORIZED",
|
|
5582
|
+
message: "Authentication required",
|
|
5583
|
+
cause: "No Clerk userId",
|
|
5584
|
+
});
|
|
5585
|
+
}
|
|
5586
|
+
{{/if}}
|
|
5187
5587
|
return next({
|
|
5188
5588
|
ctx: {
|
|
5189
5589
|
...ctx,
|
|
5590
|
+
{{#if (eq auth "better-auth")}}
|
|
5190
5591
|
session: ctx.session,
|
|
5592
|
+
{{else}}
|
|
5593
|
+
auth: ctx.auth,
|
|
5594
|
+
{{/if}}
|
|
5191
5595
|
},
|
|
5192
5596
|
});
|
|
5193
5597
|
});
|
|
@@ -5220,7 +5624,7 @@ export type AppRouter = typeof appRouter;
|
|
|
5220
5624
|
export type AppRouterClient = RouterClient<typeof appRouter>;
|
|
5221
5625
|
{{else if (eq api "trpc")}}
|
|
5222
5626
|
import {
|
|
5223
|
-
{{#if (eq auth "better-auth")}}protectedProcedure, {{/if}}publicProcedure,
|
|
5627
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}protectedProcedure, {{/if}}publicProcedure,
|
|
5224
5628
|
router,
|
|
5225
5629
|
} from "../index";
|
|
5226
5630
|
{{#if (includes examples "todo")}}
|
|
@@ -5231,11 +5635,15 @@ export const appRouter = router({
|
|
|
5231
5635
|
healthCheck: publicProcedure.query(() => {
|
|
5232
5636
|
return "OK";
|
|
5233
5637
|
}),
|
|
5234
|
-
{{#if (eq auth "better-auth")}}
|
|
5638
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
5235
5639
|
privateData: protectedProcedure.query(({ ctx }) => {
|
|
5236
5640
|
return {
|
|
5237
5641
|
message: "This is private",
|
|
5642
|
+
{{#if (eq auth "better-auth")}}
|
|
5238
5643
|
user: ctx.session.user,
|
|
5644
|
+
{{else}}
|
|
5645
|
+
userId: ctx.auth.userId,
|
|
5646
|
+
{{/if}}
|
|
5239
5647
|
};
|
|
5240
5648
|
}),
|
|
5241
5649
|
{{/if}}
|
|
@@ -5268,6 +5676,9 @@ import { toast } from 'sonner';
|
|
|
5268
5676
|
{{#unless (eq backend "self")}}
|
|
5269
5677
|
import { env } from "@{{projectName}}/env/web";
|
|
5270
5678
|
{{/unless}}
|
|
5679
|
+
{{#if (eq auth "clerk")}}
|
|
5680
|
+
import { getClerkAuthToken } from "@/utils/clerk-auth";
|
|
5681
|
+
{{/if}}
|
|
5271
5682
|
|
|
5272
5683
|
export const queryClient = new QueryClient({
|
|
5273
5684
|
queryCache: new QueryCache({
|
|
@@ -5290,6 +5701,20 @@ const trpcClient = createTRPCClient<AppRouter>({
|
|
|
5290
5701
|
{{else}}
|
|
5291
5702
|
url: \`\${env.NEXT_PUBLIC_SERVER_URL}/trpc\`,
|
|
5292
5703
|
{{/if}}
|
|
5704
|
+
{{#if (eq auth "clerk")}}
|
|
5705
|
+
headers: async () => {
|
|
5706
|
+
if (typeof window !== "undefined") {
|
|
5707
|
+
const token = await getClerkAuthToken();
|
|
5708
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
5709
|
+
}
|
|
5710
|
+
|
|
5711
|
+
const { auth } = await import("@clerk/nextjs/server");
|
|
5712
|
+
const clerkAuth = await auth();
|
|
5713
|
+
const token = await clerkAuth.getToken();
|
|
5714
|
+
|
|
5715
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
5716
|
+
},
|
|
5717
|
+
{{/if}}
|
|
5293
5718
|
{{#if (eq auth "better-auth")}}
|
|
5294
5719
|
fetch(url, options) {
|
|
5295
5720
|
return fetch(url, {
|
|
@@ -5321,6 +5746,9 @@ import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
|
|
5321
5746
|
import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
|
5322
5747
|
import { toast } from "sonner";
|
|
5323
5748
|
import { env } from "@{{projectName}}/env/web";
|
|
5749
|
+
{{#if (eq auth "clerk")}}
|
|
5750
|
+
import { getClerkAuthToken } from "@/utils/clerk-auth";
|
|
5751
|
+
{{/if}}
|
|
5324
5752
|
|
|
5325
5753
|
export const queryClient = new QueryClient({
|
|
5326
5754
|
queryCache: new QueryCache({
|
|
@@ -5339,6 +5767,12 @@ export const trpcClient = createTRPCClient<AppRouter>({
|
|
|
5339
5767
|
links: [
|
|
5340
5768
|
httpBatchLink({
|
|
5341
5769
|
url: \`\${env.VITE_SERVER_URL}/trpc\`,
|
|
5770
|
+
{{#if (eq auth "clerk")}}
|
|
5771
|
+
headers: async () => {
|
|
5772
|
+
const token = await getClerkAuthToken();
|
|
5773
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
5774
|
+
},
|
|
5775
|
+
{{/if}}
|
|
5342
5776
|
{{#if (eq auth "better-auth")}}
|
|
5343
5777
|
fetch(url, options) {
|
|
5344
5778
|
return fetch(url, {
|
|
@@ -14451,10 +14885,14 @@ export const get = query({
|
|
|
14451
14885
|
});
|
|
14452
14886
|
`],
|
|
14453
14887
|
["auth/clerk/convex/native/base/app/(auth)/_layout.tsx.hbs", `import { Redirect, Stack } from "expo-router";
|
|
14454
|
-
import { useAuth } from "@clerk/
|
|
14888
|
+
import { useAuth } from "@clerk/expo";
|
|
14455
14889
|
|
|
14456
14890
|
export default function AuthRoutesLayout() {
|
|
14457
|
-
const { isSignedIn } = useAuth();
|
|
14891
|
+
const { isLoaded, isSignedIn } = useAuth();
|
|
14892
|
+
|
|
14893
|
+
if (!isLoaded) {
|
|
14894
|
+
return null;
|
|
14895
|
+
}
|
|
14458
14896
|
|
|
14459
14897
|
if (isSignedIn) {
|
|
14460
14898
|
return <Redirect href={"/"} />;
|
|
@@ -14463,189 +14901,480 @@ export default function AuthRoutesLayout() {
|
|
|
14463
14901
|
return <Stack />;
|
|
14464
14902
|
}
|
|
14465
14903
|
`],
|
|
14466
|
-
["auth/clerk/convex/native/base/app/(auth)/sign-in.tsx.hbs", `import { useSignIn } from "@clerk/
|
|
14467
|
-
import { Link, useRouter } from "expo-router";
|
|
14468
|
-
import { Text, TextInput, TouchableOpacity, View } from "react-native";
|
|
14904
|
+
["auth/clerk/convex/native/base/app/(auth)/sign-in.tsx.hbs", `import { useSignIn } from "@clerk/expo";
|
|
14905
|
+
import { type Href, Link, useRouter } from "expo-router";
|
|
14469
14906
|
import React from "react";
|
|
14907
|
+
import { Pressable, StyleSheet, Text, TextInput, View } from "react-native";
|
|
14908
|
+
|
|
14909
|
+
function pushDecoratedUrl(router: ReturnType<typeof useRouter>, decorateUrl: (url: string) => string, href: string) {
|
|
14910
|
+
const url = decorateUrl(href);
|
|
14911
|
+
const nextHref = url.startsWith("http") ? new URL(url).pathname : url;
|
|
14912
|
+
router.push(nextHref as Href);
|
|
14913
|
+
}
|
|
14470
14914
|
|
|
14471
14915
|
export default function Page() {
|
|
14472
|
-
const { signIn,
|
|
14916
|
+
const { signIn, errors, fetchStatus } = useSignIn();
|
|
14473
14917
|
const router = useRouter();
|
|
14474
|
-
|
|
14475
14918
|
const [emailAddress, setEmailAddress] = React.useState("");
|
|
14476
14919
|
const [password, setPassword] = React.useState("");
|
|
14920
|
+
const [code, setCode] = React.useState("");
|
|
14921
|
+
const [statusMessage, setStatusMessage] = React.useState<string | null>(null);
|
|
14477
14922
|
|
|
14478
|
-
|
|
14479
|
-
|
|
14480
|
-
|
|
14923
|
+
const emailCodeFactor = signIn.supportedSecondFactors.find(
|
|
14924
|
+
(factor) => factor.strategy === "email_code",
|
|
14925
|
+
);
|
|
14926
|
+
const requiresEmailCode =
|
|
14927
|
+
signIn.status === "needs_client_trust" ||
|
|
14928
|
+
(signIn.status === "needs_second_factor" && !!emailCodeFactor);
|
|
14481
14929
|
|
|
14482
|
-
|
|
14483
|
-
|
|
14484
|
-
|
|
14485
|
-
|
|
14486
|
-
|
|
14487
|
-
|
|
14930
|
+
const handleSubmit = async () => {
|
|
14931
|
+
setStatusMessage(null);
|
|
14932
|
+
|
|
14933
|
+
const { error } = await signIn.password({
|
|
14934
|
+
emailAddress,
|
|
14935
|
+
password,
|
|
14936
|
+
});
|
|
14937
|
+
|
|
14938
|
+
if (error) {
|
|
14939
|
+
console.error(JSON.stringify(error, null, 2));
|
|
14940
|
+
setStatusMessage(error.longMessage ?? "Unable to sign in. Please try again.");
|
|
14941
|
+
return;
|
|
14942
|
+
}
|
|
14488
14943
|
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
|
|
14493
|
-
|
|
14944
|
+
if (signIn.status === "complete") {
|
|
14945
|
+
await signIn.finalize({
|
|
14946
|
+
navigate: ({ session, decorateUrl }) => {
|
|
14947
|
+
if (session?.currentTask) {
|
|
14948
|
+
console.log(session.currentTask);
|
|
14949
|
+
return;
|
|
14950
|
+
}
|
|
14951
|
+
|
|
14952
|
+
pushDecoratedUrl(router, decorateUrl, "/");
|
|
14953
|
+
},
|
|
14954
|
+
});
|
|
14955
|
+
} else if (signIn.status === "needs_second_factor" || signIn.status === "needs_client_trust") {
|
|
14956
|
+
if (emailCodeFactor) {
|
|
14957
|
+
await signIn.mfa.sendEmailCode();
|
|
14958
|
+
setStatusMessage(\`We sent a verification code to \${emailCodeFactor.safeIdentifier}.\`);
|
|
14494
14959
|
} else {
|
|
14495
|
-
|
|
14496
|
-
|
|
14497
|
-
console.error(JSON.stringify(signInAttempt, null, 2));
|
|
14960
|
+
console.error("Second factor is required, but email_code is not available:", signIn);
|
|
14961
|
+
setStatusMessage("A second factor is required, but this screen only supports email codes right now.");
|
|
14498
14962
|
}
|
|
14499
|
-
}
|
|
14500
|
-
|
|
14501
|
-
|
|
14502
|
-
|
|
14963
|
+
} else {
|
|
14964
|
+
console.error("Sign-in attempt not complete:", signIn);
|
|
14965
|
+
setStatusMessage("Sign-in could not be completed. Check the logs for more details.");
|
|
14966
|
+
}
|
|
14967
|
+
};
|
|
14968
|
+
|
|
14969
|
+
const handleVerify = async () => {
|
|
14970
|
+
setStatusMessage(null);
|
|
14971
|
+
|
|
14972
|
+
await signIn.mfa.verifyEmailCode({ code });
|
|
14973
|
+
|
|
14974
|
+
if (signIn.status === "complete") {
|
|
14975
|
+
await signIn.finalize({
|
|
14976
|
+
navigate: ({ session, decorateUrl }) => {
|
|
14977
|
+
if (session?.currentTask) {
|
|
14978
|
+
console.log(session.currentTask);
|
|
14979
|
+
return;
|
|
14980
|
+
}
|
|
14981
|
+
|
|
14982
|
+
pushDecoratedUrl(router, decorateUrl, "/");
|
|
14983
|
+
},
|
|
14984
|
+
});
|
|
14985
|
+
} else {
|
|
14986
|
+
console.error("Sign-in attempt not complete:", signIn);
|
|
14987
|
+
setStatusMessage("That code did not complete sign-in. Please try again.");
|
|
14503
14988
|
}
|
|
14504
14989
|
};
|
|
14505
14990
|
|
|
14991
|
+
if (requiresEmailCode) {
|
|
14992
|
+
return (
|
|
14993
|
+
<View style={styles.container}>
|
|
14994
|
+
<Text style={styles.title}>Verify your account</Text>
|
|
14995
|
+
{statusMessage && <Text style={styles.helper}>{statusMessage}</Text>}
|
|
14996
|
+
<TextInput
|
|
14997
|
+
style={styles.input}
|
|
14998
|
+
value={code}
|
|
14999
|
+
placeholder="Enter your verification code"
|
|
15000
|
+
placeholderTextColor="#666666"
|
|
15001
|
+
onChangeText={(value) => setCode(value)}
|
|
15002
|
+
keyboardType="numeric"
|
|
15003
|
+
/>
|
|
15004
|
+
{errors.fields.code && <Text style={styles.error}>{errors.fields.code.message}</Text>}
|
|
15005
|
+
<Pressable
|
|
15006
|
+
style={({ pressed }) => [
|
|
15007
|
+
styles.button,
|
|
15008
|
+
fetchStatus === "fetching" && styles.buttonDisabled,
|
|
15009
|
+
pressed && styles.buttonPressed,
|
|
15010
|
+
]}
|
|
15011
|
+
onPress={handleVerify}
|
|
15012
|
+
disabled={fetchStatus === "fetching"}
|
|
15013
|
+
>
|
|
15014
|
+
<Text style={styles.buttonText}>Verify</Text>
|
|
15015
|
+
</Pressable>
|
|
15016
|
+
<Pressable
|
|
15017
|
+
style={({ pressed }) => [styles.secondaryButton, pressed && styles.buttonPressed]}
|
|
15018
|
+
onPress={() => signIn.mfa.sendEmailCode()}
|
|
15019
|
+
>
|
|
15020
|
+
<Text style={styles.secondaryButtonText}>I need a new code</Text>
|
|
15021
|
+
</Pressable>
|
|
15022
|
+
</View>
|
|
15023
|
+
);
|
|
15024
|
+
}
|
|
15025
|
+
|
|
14506
15026
|
return (
|
|
14507
|
-
<View>
|
|
14508
|
-
<Text>Sign in</Text>
|
|
15027
|
+
<View style={styles.container}>
|
|
15028
|
+
<Text style={styles.title}>Sign in</Text>
|
|
15029
|
+
{statusMessage && <Text style={styles.helper}>{statusMessage}</Text>}
|
|
15030
|
+
<Text style={styles.label}>Email address</Text>
|
|
14509
15031
|
<TextInput
|
|
15032
|
+
style={styles.input}
|
|
14510
15033
|
autoCapitalize="none"
|
|
14511
15034
|
value={emailAddress}
|
|
14512
15035
|
placeholder="Enter email"
|
|
14513
|
-
|
|
15036
|
+
placeholderTextColor="#666666"
|
|
15037
|
+
onChangeText={(value) => setEmailAddress(value)}
|
|
15038
|
+
keyboardType="email-address"
|
|
14514
15039
|
/>
|
|
15040
|
+
{errors.fields.identifier && <Text style={styles.error}>{errors.fields.identifier.message}</Text>}
|
|
15041
|
+
<Text style={styles.label}>Password</Text>
|
|
14515
15042
|
<TextInput
|
|
15043
|
+
style={styles.input}
|
|
14516
15044
|
value={password}
|
|
14517
15045
|
placeholder="Enter password"
|
|
15046
|
+
placeholderTextColor="#666666"
|
|
14518
15047
|
secureTextEntry={true}
|
|
14519
|
-
onChangeText={(
|
|
15048
|
+
onChangeText={(value) => setPassword(value)}
|
|
14520
15049
|
/>
|
|
14521
|
-
<
|
|
14522
|
-
|
|
14523
|
-
|
|
14524
|
-
|
|
14525
|
-
|
|
15050
|
+
{errors.fields.password && <Text style={styles.error}>{errors.fields.password.message}</Text>}
|
|
15051
|
+
<Pressable
|
|
15052
|
+
style={({ pressed }) => [
|
|
15053
|
+
styles.button,
|
|
15054
|
+
(!emailAddress || !password || fetchStatus === "fetching") && styles.buttonDisabled,
|
|
15055
|
+
pressed && styles.buttonPressed,
|
|
15056
|
+
]}
|
|
15057
|
+
onPress={handleSubmit}
|
|
15058
|
+
disabled={!emailAddress || !password || fetchStatus === "fetching"}
|
|
15059
|
+
>
|
|
15060
|
+
<Text style={styles.buttonText}>Sign in</Text>
|
|
15061
|
+
</Pressable>
|
|
15062
|
+
<View style={styles.linkContainer}>
|
|
15063
|
+
<Text>Don't have an account? </Text>
|
|
14526
15064
|
<Link href="/sign-up">
|
|
14527
|
-
<Text>Sign up</Text>
|
|
15065
|
+
<Text style={styles.linkText}>Sign up</Text>
|
|
14528
15066
|
</Link>
|
|
14529
15067
|
</View>
|
|
14530
15068
|
</View>
|
|
14531
15069
|
);
|
|
14532
15070
|
}
|
|
15071
|
+
|
|
15072
|
+
const styles = StyleSheet.create({
|
|
15073
|
+
container: {
|
|
15074
|
+
flex: 1,
|
|
15075
|
+
padding: 20,
|
|
15076
|
+
gap: 12,
|
|
15077
|
+
},
|
|
15078
|
+
title: {
|
|
15079
|
+
marginBottom: 8,
|
|
15080
|
+
fontSize: 24,
|
|
15081
|
+
fontWeight: "700",
|
|
15082
|
+
},
|
|
15083
|
+
label: {
|
|
15084
|
+
fontWeight: "600",
|
|
15085
|
+
fontSize: 14,
|
|
15086
|
+
},
|
|
15087
|
+
input: {
|
|
15088
|
+
borderWidth: 1,
|
|
15089
|
+
borderColor: "#ccc",
|
|
15090
|
+
borderRadius: 8,
|
|
15091
|
+
padding: 12,
|
|
15092
|
+
fontSize: 16,
|
|
15093
|
+
backgroundColor: "#fff",
|
|
15094
|
+
},
|
|
15095
|
+
button: {
|
|
15096
|
+
backgroundColor: "#0a7ea4",
|
|
15097
|
+
paddingVertical: 12,
|
|
15098
|
+
paddingHorizontal: 24,
|
|
15099
|
+
borderRadius: 8,
|
|
15100
|
+
alignItems: "center",
|
|
15101
|
+
marginTop: 8,
|
|
15102
|
+
},
|
|
15103
|
+
buttonPressed: {
|
|
15104
|
+
opacity: 0.7,
|
|
15105
|
+
},
|
|
15106
|
+
buttonDisabled: {
|
|
15107
|
+
opacity: 0.5,
|
|
15108
|
+
},
|
|
15109
|
+
buttonText: {
|
|
15110
|
+
color: "#fff",
|
|
15111
|
+
fontWeight: "600",
|
|
15112
|
+
},
|
|
15113
|
+
secondaryButton: {
|
|
15114
|
+
paddingVertical: 12,
|
|
15115
|
+
paddingHorizontal: 24,
|
|
15116
|
+
borderRadius: 8,
|
|
15117
|
+
alignItems: "center",
|
|
15118
|
+
marginTop: 8,
|
|
15119
|
+
},
|
|
15120
|
+
secondaryButtonText: {
|
|
15121
|
+
color: "#0a7ea4",
|
|
15122
|
+
fontWeight: "600",
|
|
15123
|
+
},
|
|
15124
|
+
linkContainer: {
|
|
15125
|
+
flexDirection: "row",
|
|
15126
|
+
gap: 4,
|
|
15127
|
+
marginTop: 12,
|
|
15128
|
+
alignItems: "center",
|
|
15129
|
+
},
|
|
15130
|
+
linkText: {
|
|
15131
|
+
color: "#0a7ea4",
|
|
15132
|
+
fontWeight: "600",
|
|
15133
|
+
},
|
|
15134
|
+
error: {
|
|
15135
|
+
color: "#d32f2f",
|
|
15136
|
+
fontSize: 12,
|
|
15137
|
+
marginTop: -8,
|
|
15138
|
+
},
|
|
15139
|
+
helper: {
|
|
15140
|
+
color: "#555555",
|
|
15141
|
+
fontSize: 13,
|
|
15142
|
+
},
|
|
15143
|
+
});
|
|
14533
15144
|
`],
|
|
14534
|
-
["auth/clerk/convex/native/base/app/(auth)/sign-up.tsx.hbs", `import
|
|
14535
|
-
import {
|
|
14536
|
-
import
|
|
14537
|
-
import {
|
|
15145
|
+
["auth/clerk/convex/native/base/app/(auth)/sign-up.tsx.hbs", `import { useAuth, useSignUp } from "@clerk/expo";
|
|
15146
|
+
import { type Href, Link, useRouter } from "expo-router";
|
|
15147
|
+
import React from "react";
|
|
15148
|
+
import { Pressable, StyleSheet, Text, TextInput, View } from "react-native";
|
|
14538
15149
|
|
|
14539
|
-
|
|
14540
|
-
const
|
|
14541
|
-
const
|
|
15150
|
+
function pushDecoratedUrl(router: ReturnType<typeof useRouter>, decorateUrl: (url: string) => string, href: string) {
|
|
15151
|
+
const url = decorateUrl(href);
|
|
15152
|
+
const nextHref = url.startsWith("http") ? new URL(url).pathname : url;
|
|
15153
|
+
router.push(nextHref as Href);
|
|
15154
|
+
}
|
|
14542
15155
|
|
|
15156
|
+
export default function Page() {
|
|
15157
|
+
const { signUp, errors, fetchStatus } = useSignUp();
|
|
15158
|
+
const { isSignedIn } = useAuth();
|
|
15159
|
+
const router = useRouter();
|
|
14543
15160
|
const [emailAddress, setEmailAddress] = React.useState("");
|
|
14544
15161
|
const [password, setPassword] = React.useState("");
|
|
14545
|
-
const [pendingVerification, setPendingVerification] = React.useState(false);
|
|
14546
15162
|
const [code, setCode] = React.useState("");
|
|
15163
|
+
const [statusMessage, setStatusMessage] = React.useState<string | null>(null);
|
|
14547
15164
|
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
if (!isLoaded) return;
|
|
14551
|
-
|
|
14552
|
-
// Start sign-up process using email and password provided
|
|
14553
|
-
try {
|
|
14554
|
-
await signUp.create({
|
|
14555
|
-
emailAddress,
|
|
14556
|
-
password,
|
|
14557
|
-
});
|
|
15165
|
+
const handleSubmit = async () => {
|
|
15166
|
+
setStatusMessage(null);
|
|
14558
15167
|
|
|
14559
|
-
|
|
14560
|
-
|
|
15168
|
+
const { error } = await signUp.password({
|
|
15169
|
+
emailAddress,
|
|
15170
|
+
password,
|
|
15171
|
+
});
|
|
14561
15172
|
|
|
14562
|
-
|
|
14563
|
-
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
// See https://clerk.com/docs/custom-flows/error-handling
|
|
14567
|
-
// for more info on error handling
|
|
14568
|
-
console.error(JSON.stringify(err, null, 2));
|
|
15173
|
+
if (error) {
|
|
15174
|
+
console.error(JSON.stringify(error, null, 2));
|
|
15175
|
+
setStatusMessage(error.longMessage ?? "Unable to sign up. Please try again.");
|
|
15176
|
+
return;
|
|
14569
15177
|
}
|
|
15178
|
+
|
|
15179
|
+
await signUp.verifications.sendEmailCode();
|
|
15180
|
+
setStatusMessage(\`We sent a verification code to \${emailAddress}.\`);
|
|
14570
15181
|
};
|
|
14571
15182
|
|
|
14572
|
-
|
|
14573
|
-
|
|
14574
|
-
if (!isLoaded) return;
|
|
15183
|
+
const handleVerify = async () => {
|
|
15184
|
+
setStatusMessage(null);
|
|
14575
15185
|
|
|
14576
|
-
|
|
14577
|
-
|
|
14578
|
-
|
|
14579
|
-
code,
|
|
14580
|
-
});
|
|
15186
|
+
await signUp.verifications.verifyEmailCode({
|
|
15187
|
+
code,
|
|
15188
|
+
});
|
|
14581
15189
|
|
|
14582
|
-
|
|
14583
|
-
|
|
14584
|
-
|
|
14585
|
-
|
|
14586
|
-
|
|
14587
|
-
|
|
14588
|
-
|
|
14589
|
-
|
|
14590
|
-
|
|
14591
|
-
|
|
14592
|
-
|
|
14593
|
-
|
|
14594
|
-
|
|
14595
|
-
|
|
15190
|
+
if (signUp.status === "complete") {
|
|
15191
|
+
await signUp.finalize({
|
|
15192
|
+
navigate: ({ session, decorateUrl }) => {
|
|
15193
|
+
if (session?.currentTask) {
|
|
15194
|
+
console.log(session.currentTask);
|
|
15195
|
+
return;
|
|
15196
|
+
}
|
|
15197
|
+
|
|
15198
|
+
pushDecoratedUrl(router, decorateUrl, "/");
|
|
15199
|
+
},
|
|
15200
|
+
});
|
|
15201
|
+
} else {
|
|
15202
|
+
console.error("Sign-up attempt not complete:", signUp);
|
|
15203
|
+
setStatusMessage("That code did not complete sign-up. Please try again.");
|
|
14596
15204
|
}
|
|
14597
15205
|
};
|
|
14598
15206
|
|
|
14599
|
-
if (
|
|
15207
|
+
if (signUp.status === "complete" || isSignedIn) {
|
|
15208
|
+
return null;
|
|
15209
|
+
}
|
|
15210
|
+
|
|
15211
|
+
if (
|
|
15212
|
+
signUp.status === "missing_requirements" &&
|
|
15213
|
+
signUp.unverifiedFields.includes("email_address") &&
|
|
15214
|
+
signUp.missingFields.length === 0
|
|
15215
|
+
) {
|
|
14600
15216
|
return (
|
|
14601
|
-
|
|
14602
|
-
<Text>Verify your
|
|
15217
|
+
<View style={styles.container}>
|
|
15218
|
+
<Text style={styles.title}>Verify your account</Text>
|
|
15219
|
+
{statusMessage && <Text style={styles.helper}>{statusMessage}</Text>}
|
|
14603
15220
|
<TextInput
|
|
15221
|
+
style={styles.input}
|
|
14604
15222
|
value={code}
|
|
14605
15223
|
placeholder="Enter your verification code"
|
|
14606
|
-
|
|
15224
|
+
placeholderTextColor="#666666"
|
|
15225
|
+
onChangeText={(value) => setCode(value)}
|
|
15226
|
+
keyboardType="numeric"
|
|
14607
15227
|
/>
|
|
14608
|
-
<
|
|
14609
|
-
|
|
14610
|
-
|
|
14611
|
-
|
|
15228
|
+
{errors.fields.code && <Text style={styles.error}>{errors.fields.code.message}</Text>}
|
|
15229
|
+
<Pressable
|
|
15230
|
+
style={({ pressed }) => [
|
|
15231
|
+
styles.button,
|
|
15232
|
+
fetchStatus === "fetching" && styles.buttonDisabled,
|
|
15233
|
+
pressed && styles.buttonPressed,
|
|
15234
|
+
]}
|
|
15235
|
+
onPress={handleVerify}
|
|
15236
|
+
disabled={fetchStatus === "fetching"}
|
|
15237
|
+
>
|
|
15238
|
+
<Text style={styles.buttonText}>Verify</Text>
|
|
15239
|
+
</Pressable>
|
|
15240
|
+
<Pressable
|
|
15241
|
+
style={({ pressed }) => [styles.secondaryButton, pressed && styles.buttonPressed]}
|
|
15242
|
+
onPress={() => signUp.verifications.sendEmailCode()}
|
|
15243
|
+
>
|
|
15244
|
+
<Text style={styles.secondaryButtonText}>I need a new code</Text>
|
|
15245
|
+
</Pressable>
|
|
15246
|
+
</View>
|
|
14612
15247
|
);
|
|
14613
15248
|
}
|
|
14614
15249
|
|
|
14615
15250
|
return (
|
|
14616
|
-
<View>
|
|
14617
|
-
<Text>Sign up</Text>
|
|
15251
|
+
<View style={styles.container}>
|
|
15252
|
+
<Text style={styles.title}>Sign up</Text>
|
|
15253
|
+
{statusMessage && <Text style={styles.helper}>{statusMessage}</Text>}
|
|
15254
|
+
<Text style={styles.label}>Email address</Text>
|
|
14618
15255
|
<TextInput
|
|
15256
|
+
style={styles.input}
|
|
14619
15257
|
autoCapitalize="none"
|
|
14620
15258
|
value={emailAddress}
|
|
14621
15259
|
placeholder="Enter email"
|
|
14622
|
-
|
|
15260
|
+
placeholderTextColor="#666666"
|
|
15261
|
+
onChangeText={(value) => setEmailAddress(value)}
|
|
15262
|
+
keyboardType="email-address"
|
|
14623
15263
|
/>
|
|
15264
|
+
{errors.fields.emailAddress && (
|
|
15265
|
+
<Text style={styles.error}>{errors.fields.emailAddress.message}</Text>
|
|
15266
|
+
)}
|
|
15267
|
+
<Text style={styles.label}>Password</Text>
|
|
14624
15268
|
<TextInput
|
|
15269
|
+
style={styles.input}
|
|
14625
15270
|
value={password}
|
|
14626
15271
|
placeholder="Enter password"
|
|
15272
|
+
placeholderTextColor="#666666"
|
|
14627
15273
|
secureTextEntry={true}
|
|
14628
|
-
onChangeText={(
|
|
15274
|
+
onChangeText={(value) => setPassword(value)}
|
|
14629
15275
|
/>
|
|
14630
|
-
<
|
|
14631
|
-
|
|
14632
|
-
|
|
14633
|
-
|
|
14634
|
-
|
|
15276
|
+
{errors.fields.password && <Text style={styles.error}>{errors.fields.password.message}</Text>}
|
|
15277
|
+
<Pressable
|
|
15278
|
+
style={({ pressed }) => [
|
|
15279
|
+
styles.button,
|
|
15280
|
+
(!emailAddress || !password || fetchStatus === "fetching") && styles.buttonDisabled,
|
|
15281
|
+
pressed && styles.buttonPressed,
|
|
15282
|
+
]}
|
|
15283
|
+
onPress={handleSubmit}
|
|
15284
|
+
disabled={!emailAddress || !password || fetchStatus === "fetching"}
|
|
15285
|
+
>
|
|
15286
|
+
<Text style={styles.buttonText}>Sign up</Text>
|
|
15287
|
+
</Pressable>
|
|
15288
|
+
<View style={styles.linkContainer}>
|
|
15289
|
+
<Text>Already have an account? </Text>
|
|
14635
15290
|
<Link href="/sign-in">
|
|
14636
|
-
<Text>Sign in</Text>
|
|
15291
|
+
<Text style={styles.linkText}>Sign in</Text>
|
|
14637
15292
|
</Link>
|
|
14638
15293
|
</View>
|
|
15294
|
+
<View nativeID="clerk-captcha" />
|
|
14639
15295
|
</View>
|
|
14640
15296
|
);
|
|
14641
15297
|
}
|
|
14642
|
-
`],
|
|
14643
|
-
["auth/clerk/convex/native/base/components/sign-out-button.tsx.hbs", `import { useClerk } from "@clerk/clerk-expo";
|
|
14644
|
-
import { useRouter } from "expo-router";
|
|
14645
|
-
import { Text, TouchableOpacity } from "react-native";
|
|
14646
15298
|
|
|
14647
|
-
|
|
14648
|
-
|
|
15299
|
+
const styles = StyleSheet.create({
|
|
15300
|
+
container: {
|
|
15301
|
+
flex: 1,
|
|
15302
|
+
padding: 20,
|
|
15303
|
+
gap: 12,
|
|
15304
|
+
},
|
|
15305
|
+
title: {
|
|
15306
|
+
marginBottom: 8,
|
|
15307
|
+
fontSize: 24,
|
|
15308
|
+
fontWeight: "700",
|
|
15309
|
+
},
|
|
15310
|
+
label: {
|
|
15311
|
+
fontWeight: "600",
|
|
15312
|
+
fontSize: 14,
|
|
15313
|
+
},
|
|
15314
|
+
input: {
|
|
15315
|
+
borderWidth: 1,
|
|
15316
|
+
borderColor: "#ccc",
|
|
15317
|
+
borderRadius: 8,
|
|
15318
|
+
padding: 12,
|
|
15319
|
+
fontSize: 16,
|
|
15320
|
+
backgroundColor: "#fff",
|
|
15321
|
+
},
|
|
15322
|
+
button: {
|
|
15323
|
+
backgroundColor: "#0a7ea4",
|
|
15324
|
+
paddingVertical: 12,
|
|
15325
|
+
paddingHorizontal: 24,
|
|
15326
|
+
borderRadius: 8,
|
|
15327
|
+
alignItems: "center",
|
|
15328
|
+
marginTop: 8,
|
|
15329
|
+
},
|
|
15330
|
+
buttonPressed: {
|
|
15331
|
+
opacity: 0.7,
|
|
15332
|
+
},
|
|
15333
|
+
buttonDisabled: {
|
|
15334
|
+
opacity: 0.5,
|
|
15335
|
+
},
|
|
15336
|
+
buttonText: {
|
|
15337
|
+
color: "#fff",
|
|
15338
|
+
fontWeight: "600",
|
|
15339
|
+
},
|
|
15340
|
+
secondaryButton: {
|
|
15341
|
+
paddingVertical: 12,
|
|
15342
|
+
paddingHorizontal: 24,
|
|
15343
|
+
borderRadius: 8,
|
|
15344
|
+
alignItems: "center",
|
|
15345
|
+
marginTop: 8,
|
|
15346
|
+
},
|
|
15347
|
+
secondaryButtonText: {
|
|
15348
|
+
color: "#0a7ea4",
|
|
15349
|
+
fontWeight: "600",
|
|
15350
|
+
},
|
|
15351
|
+
linkContainer: {
|
|
15352
|
+
flexDirection: "row",
|
|
15353
|
+
gap: 4,
|
|
15354
|
+
marginTop: 12,
|
|
15355
|
+
alignItems: "center",
|
|
15356
|
+
},
|
|
15357
|
+
linkText: {
|
|
15358
|
+
color: "#0a7ea4",
|
|
15359
|
+
fontWeight: "600",
|
|
15360
|
+
},
|
|
15361
|
+
error: {
|
|
15362
|
+
color: "#d32f2f",
|
|
15363
|
+
fontSize: 12,
|
|
15364
|
+
marginTop: -8,
|
|
15365
|
+
},
|
|
15366
|
+
helper: {
|
|
15367
|
+
color: "#555555",
|
|
15368
|
+
fontSize: 13,
|
|
15369
|
+
},
|
|
15370
|
+
});
|
|
15371
|
+
`],
|
|
15372
|
+
["auth/clerk/convex/native/base/components/sign-out-button.tsx.hbs", `import { useClerk } from "@clerk/expo";
|
|
15373
|
+
import { useRouter } from "expo-router";
|
|
15374
|
+
import { Text, TouchableOpacity } from "react-native";
|
|
15375
|
+
|
|
15376
|
+
export const SignOutButton = () => {
|
|
15377
|
+
// Use \`useClerk()\` to access the \`signOut()\` function
|
|
14649
15378
|
const { signOut } = useClerk();
|
|
14650
15379
|
const router = useRouter();
|
|
14651
15380
|
|
|
@@ -14670,35 +15399,757 @@ export const SignOutButton = () => {
|
|
|
14670
15399
|
`],
|
|
14671
15400
|
["auth/clerk/convex/web/react/next/src/app/dashboard/page.tsx.hbs", `"use client";
|
|
14672
15401
|
|
|
14673
|
-
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
14674
|
-
import { SignInButton, UserButton, useUser } from "@clerk/nextjs";
|
|
14675
|
-
import { Authenticated, AuthLoading, Unauthenticated, useQuery } from "convex/react";
|
|
15402
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
15403
|
+
import { SignInButton, UserButton, useUser } from "@clerk/nextjs";
|
|
15404
|
+
import { Authenticated, AuthLoading, Unauthenticated, useQuery } from "convex/react";
|
|
15405
|
+
|
|
15406
|
+
export default function Dashboard() {
|
|
15407
|
+
const user = useUser();
|
|
15408
|
+
const privateData = useQuery(api.privateData.get);
|
|
15409
|
+
|
|
15410
|
+
return (
|
|
15411
|
+
<>
|
|
15412
|
+
<Authenticated>
|
|
15413
|
+
<div>
|
|
15414
|
+
<h1>Dashboard</h1>
|
|
15415
|
+
<p>Welcome {user.user?.fullName}</p>
|
|
15416
|
+
<p>privateData: {privateData?.message}</p>
|
|
15417
|
+
<UserButton />
|
|
15418
|
+
</div>
|
|
15419
|
+
</Authenticated>
|
|
15420
|
+
<Unauthenticated>
|
|
15421
|
+
<SignInButton />
|
|
15422
|
+
</Unauthenticated>
|
|
15423
|
+
<AuthLoading>
|
|
15424
|
+
<div>Loading...</div>
|
|
15425
|
+
</AuthLoading>
|
|
15426
|
+
</>
|
|
15427
|
+
);
|
|
15428
|
+
}
|
|
15429
|
+
`],
|
|
15430
|
+
["auth/clerk/convex/web/react/next/src/proxy.ts.hbs", `import { clerkMiddleware } from "@clerk/nextjs/server";
|
|
15431
|
+
|
|
15432
|
+
export default clerkMiddleware();
|
|
15433
|
+
|
|
15434
|
+
export const config = {
|
|
15435
|
+
matcher: [
|
|
15436
|
+
// Skip Next.js internals and all static files, unless found in search params
|
|
15437
|
+
"/((?!_next|[^?]*\\\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
|
|
15438
|
+
// Always run for API routes
|
|
15439
|
+
"/(api|trpc)(.*)",
|
|
15440
|
+
],
|
|
15441
|
+
};
|
|
15442
|
+
`],
|
|
15443
|
+
["auth/clerk/convex/web/react/react-router/src/routes/dashboard.tsx.hbs", `import { SignInButton, UserButton, useUser } from "@clerk/react-router";
|
|
15444
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
15445
|
+
import {
|
|
15446
|
+
Authenticated,
|
|
15447
|
+
AuthLoading,
|
|
15448
|
+
Unauthenticated,
|
|
15449
|
+
useQuery,
|
|
15450
|
+
} from "convex/react";
|
|
15451
|
+
|
|
15452
|
+
export default function Dashboard() {
|
|
15453
|
+
const privateData = useQuery(api.privateData.get);
|
|
15454
|
+
const user = useUser();
|
|
15455
|
+
|
|
15456
|
+
return (
|
|
15457
|
+
<>
|
|
15458
|
+
<Authenticated>
|
|
15459
|
+
<div>
|
|
15460
|
+
<h1>Dashboard</h1>
|
|
15461
|
+
<p>Welcome {user.user?.fullName}</p>
|
|
15462
|
+
<p>privateData: {privateData?.message}</p>
|
|
15463
|
+
<UserButton />
|
|
15464
|
+
</div>
|
|
15465
|
+
</Authenticated>
|
|
15466
|
+
<Unauthenticated>
|
|
15467
|
+
<SignInButton />
|
|
15468
|
+
</Unauthenticated>
|
|
15469
|
+
<AuthLoading>
|
|
15470
|
+
<div>Loading...</div>
|
|
15471
|
+
</AuthLoading>
|
|
15472
|
+
</>
|
|
15473
|
+
);
|
|
15474
|
+
}
|
|
15475
|
+
`],
|
|
15476
|
+
["auth/clerk/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs", `import { SignInButton, UserButton, useUser } from "@clerk/react";
|
|
15477
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
15478
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
15479
|
+
import {
|
|
15480
|
+
Authenticated,
|
|
15481
|
+
AuthLoading,
|
|
15482
|
+
Unauthenticated,
|
|
15483
|
+
useQuery,
|
|
15484
|
+
} from "convex/react";
|
|
15485
|
+
|
|
15486
|
+
export const Route = createFileRoute("/dashboard")({
|
|
15487
|
+
component: RouteComponent,
|
|
15488
|
+
});
|
|
15489
|
+
|
|
15490
|
+
function RouteComponent() {
|
|
15491
|
+
const privateData = useQuery(api.privateData.get);
|
|
15492
|
+
const user = useUser()
|
|
15493
|
+
|
|
15494
|
+
return (
|
|
15495
|
+
<>
|
|
15496
|
+
<Authenticated>
|
|
15497
|
+
<div>
|
|
15498
|
+
<h1>Dashboard</h1>
|
|
15499
|
+
<p>Welcome {user.user?.fullName}</p>
|
|
15500
|
+
<p>privateData: {privateData?.message}</p>
|
|
15501
|
+
<UserButton />
|
|
15502
|
+
</div>
|
|
15503
|
+
</Authenticated>
|
|
15504
|
+
<Unauthenticated>
|
|
15505
|
+
<SignInButton />
|
|
15506
|
+
</Unauthenticated>
|
|
15507
|
+
<AuthLoading>
|
|
15508
|
+
<div>Loading...</div>
|
|
15509
|
+
</AuthLoading>
|
|
15510
|
+
</>
|
|
15511
|
+
);
|
|
15512
|
+
}
|
|
15513
|
+
`],
|
|
15514
|
+
["auth/clerk/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs", `import { SignInButton, UserButton, useUser } from "@clerk/tanstack-react-start";
|
|
15515
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
15516
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
15517
|
+
import {
|
|
15518
|
+
Authenticated,
|
|
15519
|
+
AuthLoading,
|
|
15520
|
+
Unauthenticated,
|
|
15521
|
+
useQuery,
|
|
15522
|
+
} from "convex/react";
|
|
15523
|
+
|
|
15524
|
+
export const Route = createFileRoute("/dashboard")({
|
|
15525
|
+
component: RouteComponent,
|
|
15526
|
+
});
|
|
15527
|
+
|
|
15528
|
+
function RouteComponent() {
|
|
15529
|
+
const privateData = useQuery(api.privateData.get);
|
|
15530
|
+
const user = useUser();
|
|
15531
|
+
|
|
15532
|
+
return (
|
|
15533
|
+
<>
|
|
15534
|
+
<Authenticated>
|
|
15535
|
+
<div>
|
|
15536
|
+
<h1>Dashboard</h1>
|
|
15537
|
+
<p>Welcome {user.user?.fullName}</p>
|
|
15538
|
+
<p>privateData: {privateData?.message}</p>
|
|
15539
|
+
<UserButton />
|
|
15540
|
+
</div>
|
|
15541
|
+
</Authenticated>
|
|
15542
|
+
<Unauthenticated>
|
|
15543
|
+
<SignInButton />
|
|
15544
|
+
</Unauthenticated>
|
|
15545
|
+
<AuthLoading>
|
|
15546
|
+
<div>Loading...</div>
|
|
15547
|
+
</AuthLoading>
|
|
15548
|
+
</>
|
|
15549
|
+
);
|
|
15550
|
+
}
|
|
15551
|
+
`],
|
|
15552
|
+
["auth/clerk/convex/web/react/tanstack-start/src/start.ts.hbs", `import { clerkMiddleware } from '@clerk/tanstack-react-start/server'
|
|
15553
|
+
import { createStart } from '@tanstack/react-start'
|
|
15554
|
+
|
|
15555
|
+
export const startInstance = createStart(() => {
|
|
15556
|
+
return {
|
|
15557
|
+
requestMiddleware: [clerkMiddleware()],
|
|
15558
|
+
}
|
|
15559
|
+
})`],
|
|
15560
|
+
["auth/clerk/native/base/app/(auth)/_layout.tsx.hbs", `import { Redirect, Stack } from "expo-router";
|
|
15561
|
+
import { useAuth } from "@clerk/expo";
|
|
15562
|
+
|
|
15563
|
+
export default function AuthRoutesLayout() {
|
|
15564
|
+
const { isLoaded, isSignedIn } = useAuth();
|
|
15565
|
+
|
|
15566
|
+
if (!isLoaded) {
|
|
15567
|
+
return null;
|
|
15568
|
+
}
|
|
15569
|
+
|
|
15570
|
+
if (isSignedIn) {
|
|
15571
|
+
return <Redirect href={"/"} />;
|
|
15572
|
+
}
|
|
15573
|
+
|
|
15574
|
+
return <Stack />;
|
|
15575
|
+
}
|
|
15576
|
+
`],
|
|
15577
|
+
["auth/clerk/native/base/app/(auth)/sign-in.tsx.hbs", `import { useSignIn } from "@clerk/expo";
|
|
15578
|
+
import { type Href, Link, useRouter } from "expo-router";
|
|
15579
|
+
import React from "react";
|
|
15580
|
+
import { Pressable, StyleSheet, Text, TextInput, View } from "react-native";
|
|
15581
|
+
|
|
15582
|
+
function pushDecoratedUrl(router: ReturnType<typeof useRouter>, decorateUrl: (url: string) => string, href: string) {
|
|
15583
|
+
const url = decorateUrl(href);
|
|
15584
|
+
const nextHref = url.startsWith("http") ? new URL(url).pathname : url;
|
|
15585
|
+
router.push(nextHref as Href);
|
|
15586
|
+
}
|
|
15587
|
+
|
|
15588
|
+
export default function Page() {
|
|
15589
|
+
const { signIn, errors, fetchStatus } = useSignIn();
|
|
15590
|
+
const router = useRouter();
|
|
15591
|
+
const [emailAddress, setEmailAddress] = React.useState("");
|
|
15592
|
+
const [password, setPassword] = React.useState("");
|
|
15593
|
+
const [code, setCode] = React.useState("");
|
|
15594
|
+
const [statusMessage, setStatusMessage] = React.useState<string | null>(null);
|
|
15595
|
+
|
|
15596
|
+
const emailCodeFactor = signIn.supportedSecondFactors.find(
|
|
15597
|
+
(factor) => factor.strategy === "email_code",
|
|
15598
|
+
);
|
|
15599
|
+
const requiresEmailCode =
|
|
15600
|
+
signIn.status === "needs_client_trust" ||
|
|
15601
|
+
(signIn.status === "needs_second_factor" && !!emailCodeFactor);
|
|
15602
|
+
|
|
15603
|
+
const handleSubmit = async () => {
|
|
15604
|
+
setStatusMessage(null);
|
|
15605
|
+
|
|
15606
|
+
const { error } = await signIn.password({
|
|
15607
|
+
emailAddress,
|
|
15608
|
+
password,
|
|
15609
|
+
});
|
|
15610
|
+
|
|
15611
|
+
if (error) {
|
|
15612
|
+
console.error(JSON.stringify(error, null, 2));
|
|
15613
|
+
setStatusMessage(error.longMessage ?? "Unable to sign in. Please try again.");
|
|
15614
|
+
return;
|
|
15615
|
+
}
|
|
15616
|
+
|
|
15617
|
+
if (signIn.status === "complete") {
|
|
15618
|
+
await signIn.finalize({
|
|
15619
|
+
navigate: ({ session, decorateUrl }) => {
|
|
15620
|
+
if (session?.currentTask) {
|
|
15621
|
+
console.log(session.currentTask);
|
|
15622
|
+
return;
|
|
15623
|
+
}
|
|
15624
|
+
|
|
15625
|
+
pushDecoratedUrl(router, decorateUrl, "/");
|
|
15626
|
+
},
|
|
15627
|
+
});
|
|
15628
|
+
} else if (signIn.status === "needs_second_factor" || signIn.status === "needs_client_trust") {
|
|
15629
|
+
if (emailCodeFactor) {
|
|
15630
|
+
await signIn.mfa.sendEmailCode();
|
|
15631
|
+
setStatusMessage(\`We sent a verification code to \${emailCodeFactor.safeIdentifier}.\`);
|
|
15632
|
+
} else {
|
|
15633
|
+
console.error("Second factor is required, but email_code is not available:", signIn);
|
|
15634
|
+
setStatusMessage("A second factor is required, but this screen only supports email codes right now.");
|
|
15635
|
+
}
|
|
15636
|
+
} else {
|
|
15637
|
+
console.error("Sign-in attempt not complete:", signIn);
|
|
15638
|
+
setStatusMessage("Sign-in could not be completed. Check the logs for more details.");
|
|
15639
|
+
}
|
|
15640
|
+
};
|
|
15641
|
+
|
|
15642
|
+
const handleVerify = async () => {
|
|
15643
|
+
setStatusMessage(null);
|
|
15644
|
+
|
|
15645
|
+
await signIn.mfa.verifyEmailCode({ code });
|
|
15646
|
+
|
|
15647
|
+
if (signIn.status === "complete") {
|
|
15648
|
+
await signIn.finalize({
|
|
15649
|
+
navigate: ({ session, decorateUrl }) => {
|
|
15650
|
+
if (session?.currentTask) {
|
|
15651
|
+
console.log(session.currentTask);
|
|
15652
|
+
return;
|
|
15653
|
+
}
|
|
15654
|
+
|
|
15655
|
+
pushDecoratedUrl(router, decorateUrl, "/");
|
|
15656
|
+
},
|
|
15657
|
+
});
|
|
15658
|
+
} else {
|
|
15659
|
+
console.error("Sign-in attempt not complete:", signIn);
|
|
15660
|
+
setStatusMessage("That code did not complete sign-in. Please try again.");
|
|
15661
|
+
}
|
|
15662
|
+
};
|
|
15663
|
+
|
|
15664
|
+
if (requiresEmailCode) {
|
|
15665
|
+
return (
|
|
15666
|
+
<View style={styles.container}>
|
|
15667
|
+
<Text style={styles.title}>Verify your account</Text>
|
|
15668
|
+
{statusMessage && <Text style={styles.helper}>{statusMessage}</Text>}
|
|
15669
|
+
<TextInput
|
|
15670
|
+
style={styles.input}
|
|
15671
|
+
value={code}
|
|
15672
|
+
placeholder="Enter your verification code"
|
|
15673
|
+
placeholderTextColor="#666666"
|
|
15674
|
+
onChangeText={(value) => setCode(value)}
|
|
15675
|
+
keyboardType="numeric"
|
|
15676
|
+
/>
|
|
15677
|
+
{errors.fields.code && <Text style={styles.error}>{errors.fields.code.message}</Text>}
|
|
15678
|
+
<Pressable
|
|
15679
|
+
style={({ pressed }) => [
|
|
15680
|
+
styles.button,
|
|
15681
|
+
fetchStatus === "fetching" && styles.buttonDisabled,
|
|
15682
|
+
pressed && styles.buttonPressed,
|
|
15683
|
+
]}
|
|
15684
|
+
onPress={handleVerify}
|
|
15685
|
+
disabled={fetchStatus === "fetching"}
|
|
15686
|
+
>
|
|
15687
|
+
<Text style={styles.buttonText}>Verify</Text>
|
|
15688
|
+
</Pressable>
|
|
15689
|
+
<Pressable
|
|
15690
|
+
style={({ pressed }) => [styles.secondaryButton, pressed && styles.buttonPressed]}
|
|
15691
|
+
onPress={() => signIn.mfa.sendEmailCode()}
|
|
15692
|
+
>
|
|
15693
|
+
<Text style={styles.secondaryButtonText}>I need a new code</Text>
|
|
15694
|
+
</Pressable>
|
|
15695
|
+
</View>
|
|
15696
|
+
);
|
|
15697
|
+
}
|
|
15698
|
+
|
|
15699
|
+
return (
|
|
15700
|
+
<View style={styles.container}>
|
|
15701
|
+
<Text style={styles.title}>Sign in</Text>
|
|
15702
|
+
{statusMessage && <Text style={styles.helper}>{statusMessage}</Text>}
|
|
15703
|
+
<Text style={styles.label}>Email address</Text>
|
|
15704
|
+
<TextInput
|
|
15705
|
+
style={styles.input}
|
|
15706
|
+
autoCapitalize="none"
|
|
15707
|
+
value={emailAddress}
|
|
15708
|
+
placeholder="Enter email"
|
|
15709
|
+
placeholderTextColor="#666666"
|
|
15710
|
+
onChangeText={(value) => setEmailAddress(value)}
|
|
15711
|
+
keyboardType="email-address"
|
|
15712
|
+
/>
|
|
15713
|
+
{errors.fields.identifier && <Text style={styles.error}>{errors.fields.identifier.message}</Text>}
|
|
15714
|
+
<Text style={styles.label}>Password</Text>
|
|
15715
|
+
<TextInput
|
|
15716
|
+
style={styles.input}
|
|
15717
|
+
value={password}
|
|
15718
|
+
placeholder="Enter password"
|
|
15719
|
+
placeholderTextColor="#666666"
|
|
15720
|
+
secureTextEntry={true}
|
|
15721
|
+
onChangeText={(value) => setPassword(value)}
|
|
15722
|
+
/>
|
|
15723
|
+
{errors.fields.password && <Text style={styles.error}>{errors.fields.password.message}</Text>}
|
|
15724
|
+
<Pressable
|
|
15725
|
+
style={({ pressed }) => [
|
|
15726
|
+
styles.button,
|
|
15727
|
+
(!emailAddress || !password || fetchStatus === "fetching") && styles.buttonDisabled,
|
|
15728
|
+
pressed && styles.buttonPressed,
|
|
15729
|
+
]}
|
|
15730
|
+
onPress={handleSubmit}
|
|
15731
|
+
disabled={!emailAddress || !password || fetchStatus === "fetching"}
|
|
15732
|
+
>
|
|
15733
|
+
<Text style={styles.buttonText}>Sign in</Text>
|
|
15734
|
+
</Pressable>
|
|
15735
|
+
<View style={styles.linkContainer}>
|
|
15736
|
+
<Text>Don't have an account? </Text>
|
|
15737
|
+
<Link href="/sign-up">
|
|
15738
|
+
<Text style={styles.linkText}>Sign up</Text>
|
|
15739
|
+
</Link>
|
|
15740
|
+
</View>
|
|
15741
|
+
</View>
|
|
15742
|
+
);
|
|
15743
|
+
}
|
|
15744
|
+
|
|
15745
|
+
const styles = StyleSheet.create({
|
|
15746
|
+
container: {
|
|
15747
|
+
flex: 1,
|
|
15748
|
+
padding: 20,
|
|
15749
|
+
gap: 12,
|
|
15750
|
+
},
|
|
15751
|
+
title: {
|
|
15752
|
+
marginBottom: 8,
|
|
15753
|
+
fontSize: 24,
|
|
15754
|
+
fontWeight: "700",
|
|
15755
|
+
},
|
|
15756
|
+
label: {
|
|
15757
|
+
fontWeight: "600",
|
|
15758
|
+
fontSize: 14,
|
|
15759
|
+
},
|
|
15760
|
+
input: {
|
|
15761
|
+
borderWidth: 1,
|
|
15762
|
+
borderColor: "#ccc",
|
|
15763
|
+
borderRadius: 8,
|
|
15764
|
+
padding: 12,
|
|
15765
|
+
fontSize: 16,
|
|
15766
|
+
backgroundColor: "#fff",
|
|
15767
|
+
},
|
|
15768
|
+
button: {
|
|
15769
|
+
backgroundColor: "#0a7ea4",
|
|
15770
|
+
paddingVertical: 12,
|
|
15771
|
+
paddingHorizontal: 24,
|
|
15772
|
+
borderRadius: 8,
|
|
15773
|
+
alignItems: "center",
|
|
15774
|
+
marginTop: 8,
|
|
15775
|
+
},
|
|
15776
|
+
buttonPressed: {
|
|
15777
|
+
opacity: 0.7,
|
|
15778
|
+
},
|
|
15779
|
+
buttonDisabled: {
|
|
15780
|
+
opacity: 0.5,
|
|
15781
|
+
},
|
|
15782
|
+
buttonText: {
|
|
15783
|
+
color: "#fff",
|
|
15784
|
+
fontWeight: "600",
|
|
15785
|
+
},
|
|
15786
|
+
secondaryButton: {
|
|
15787
|
+
paddingVertical: 12,
|
|
15788
|
+
paddingHorizontal: 24,
|
|
15789
|
+
borderRadius: 8,
|
|
15790
|
+
alignItems: "center",
|
|
15791
|
+
marginTop: 8,
|
|
15792
|
+
},
|
|
15793
|
+
secondaryButtonText: {
|
|
15794
|
+
color: "#0a7ea4",
|
|
15795
|
+
fontWeight: "600",
|
|
15796
|
+
},
|
|
15797
|
+
linkContainer: {
|
|
15798
|
+
flexDirection: "row",
|
|
15799
|
+
gap: 4,
|
|
15800
|
+
marginTop: 12,
|
|
15801
|
+
alignItems: "center",
|
|
15802
|
+
},
|
|
15803
|
+
linkText: {
|
|
15804
|
+
color: "#0a7ea4",
|
|
15805
|
+
fontWeight: "600",
|
|
15806
|
+
},
|
|
15807
|
+
error: {
|
|
15808
|
+
color: "#d32f2f",
|
|
15809
|
+
fontSize: 12,
|
|
15810
|
+
marginTop: -8,
|
|
15811
|
+
},
|
|
15812
|
+
helper: {
|
|
15813
|
+
color: "#555555",
|
|
15814
|
+
fontSize: 13,
|
|
15815
|
+
},
|
|
15816
|
+
});
|
|
15817
|
+
`],
|
|
15818
|
+
["auth/clerk/native/base/app/(auth)/sign-up.tsx.hbs", `import { useAuth, useSignUp } from "@clerk/expo";
|
|
15819
|
+
import { type Href, Link, useRouter } from "expo-router";
|
|
15820
|
+
import React from "react";
|
|
15821
|
+
import { Pressable, StyleSheet, Text, TextInput, View } from "react-native";
|
|
15822
|
+
|
|
15823
|
+
function pushDecoratedUrl(router: ReturnType<typeof useRouter>, decorateUrl: (url: string) => string, href: string) {
|
|
15824
|
+
const url = decorateUrl(href);
|
|
15825
|
+
const nextHref = url.startsWith("http") ? new URL(url).pathname : url;
|
|
15826
|
+
router.push(nextHref as Href);
|
|
15827
|
+
}
|
|
15828
|
+
|
|
15829
|
+
export default function Page() {
|
|
15830
|
+
const { signUp, errors, fetchStatus } = useSignUp();
|
|
15831
|
+
const { isSignedIn } = useAuth();
|
|
15832
|
+
const router = useRouter();
|
|
15833
|
+
const [emailAddress, setEmailAddress] = React.useState("");
|
|
15834
|
+
const [password, setPassword] = React.useState("");
|
|
15835
|
+
const [code, setCode] = React.useState("");
|
|
15836
|
+
const [statusMessage, setStatusMessage] = React.useState<string | null>(null);
|
|
15837
|
+
|
|
15838
|
+
const handleSubmit = async () => {
|
|
15839
|
+
setStatusMessage(null);
|
|
15840
|
+
|
|
15841
|
+
const { error } = await signUp.password({
|
|
15842
|
+
emailAddress,
|
|
15843
|
+
password,
|
|
15844
|
+
});
|
|
15845
|
+
|
|
15846
|
+
if (error) {
|
|
15847
|
+
console.error(JSON.stringify(error, null, 2));
|
|
15848
|
+
setStatusMessage(error.longMessage ?? "Unable to sign up. Please try again.");
|
|
15849
|
+
return;
|
|
15850
|
+
}
|
|
15851
|
+
|
|
15852
|
+
await signUp.verifications.sendEmailCode();
|
|
15853
|
+
setStatusMessage(\`We sent a verification code to \${emailAddress}.\`);
|
|
15854
|
+
};
|
|
15855
|
+
|
|
15856
|
+
const handleVerify = async () => {
|
|
15857
|
+
setStatusMessage(null);
|
|
15858
|
+
|
|
15859
|
+
await signUp.verifications.verifyEmailCode({
|
|
15860
|
+
code,
|
|
15861
|
+
});
|
|
15862
|
+
|
|
15863
|
+
if (signUp.status === "complete") {
|
|
15864
|
+
await signUp.finalize({
|
|
15865
|
+
navigate: ({ session, decorateUrl }) => {
|
|
15866
|
+
if (session?.currentTask) {
|
|
15867
|
+
console.log(session.currentTask);
|
|
15868
|
+
return;
|
|
15869
|
+
}
|
|
15870
|
+
|
|
15871
|
+
pushDecoratedUrl(router, decorateUrl, "/");
|
|
15872
|
+
},
|
|
15873
|
+
});
|
|
15874
|
+
} else {
|
|
15875
|
+
console.error("Sign-up attempt not complete:", signUp);
|
|
15876
|
+
setStatusMessage("That code did not complete sign-up. Please try again.");
|
|
15877
|
+
}
|
|
15878
|
+
};
|
|
15879
|
+
|
|
15880
|
+
if (signUp.status === "complete" || isSignedIn) {
|
|
15881
|
+
return null;
|
|
15882
|
+
}
|
|
15883
|
+
|
|
15884
|
+
if (
|
|
15885
|
+
signUp.status === "missing_requirements" &&
|
|
15886
|
+
signUp.unverifiedFields.includes("email_address") &&
|
|
15887
|
+
signUp.missingFields.length === 0
|
|
15888
|
+
) {
|
|
15889
|
+
return (
|
|
15890
|
+
<View style={styles.container}>
|
|
15891
|
+
<Text style={styles.title}>Verify your account</Text>
|
|
15892
|
+
{statusMessage && <Text style={styles.helper}>{statusMessage}</Text>}
|
|
15893
|
+
<TextInput
|
|
15894
|
+
style={styles.input}
|
|
15895
|
+
value={code}
|
|
15896
|
+
placeholder="Enter your verification code"
|
|
15897
|
+
placeholderTextColor="#666666"
|
|
15898
|
+
onChangeText={(value) => setCode(value)}
|
|
15899
|
+
keyboardType="numeric"
|
|
15900
|
+
/>
|
|
15901
|
+
{errors.fields.code && <Text style={styles.error}>{errors.fields.code.message}</Text>}
|
|
15902
|
+
<Pressable
|
|
15903
|
+
style={({ pressed }) => [
|
|
15904
|
+
styles.button,
|
|
15905
|
+
fetchStatus === "fetching" && styles.buttonDisabled,
|
|
15906
|
+
pressed && styles.buttonPressed,
|
|
15907
|
+
]}
|
|
15908
|
+
onPress={handleVerify}
|
|
15909
|
+
disabled={fetchStatus === "fetching"}
|
|
15910
|
+
>
|
|
15911
|
+
<Text style={styles.buttonText}>Verify</Text>
|
|
15912
|
+
</Pressable>
|
|
15913
|
+
<Pressable
|
|
15914
|
+
style={({ pressed }) => [styles.secondaryButton, pressed && styles.buttonPressed]}
|
|
15915
|
+
onPress={() => signUp.verifications.sendEmailCode()}
|
|
15916
|
+
>
|
|
15917
|
+
<Text style={styles.secondaryButtonText}>I need a new code</Text>
|
|
15918
|
+
</Pressable>
|
|
15919
|
+
</View>
|
|
15920
|
+
);
|
|
15921
|
+
}
|
|
15922
|
+
|
|
15923
|
+
return (
|
|
15924
|
+
<View style={styles.container}>
|
|
15925
|
+
<Text style={styles.title}>Sign up</Text>
|
|
15926
|
+
{statusMessage && <Text style={styles.helper}>{statusMessage}</Text>}
|
|
15927
|
+
<Text style={styles.label}>Email address</Text>
|
|
15928
|
+
<TextInput
|
|
15929
|
+
style={styles.input}
|
|
15930
|
+
autoCapitalize="none"
|
|
15931
|
+
value={emailAddress}
|
|
15932
|
+
placeholder="Enter email"
|
|
15933
|
+
placeholderTextColor="#666666"
|
|
15934
|
+
onChangeText={(value) => setEmailAddress(value)}
|
|
15935
|
+
keyboardType="email-address"
|
|
15936
|
+
/>
|
|
15937
|
+
{errors.fields.emailAddress && (
|
|
15938
|
+
<Text style={styles.error}>{errors.fields.emailAddress.message}</Text>
|
|
15939
|
+
)}
|
|
15940
|
+
<Text style={styles.label}>Password</Text>
|
|
15941
|
+
<TextInput
|
|
15942
|
+
style={styles.input}
|
|
15943
|
+
value={password}
|
|
15944
|
+
placeholder="Enter password"
|
|
15945
|
+
placeholderTextColor="#666666"
|
|
15946
|
+
secureTextEntry={true}
|
|
15947
|
+
onChangeText={(value) => setPassword(value)}
|
|
15948
|
+
/>
|
|
15949
|
+
{errors.fields.password && <Text style={styles.error}>{errors.fields.password.message}</Text>}
|
|
15950
|
+
<Pressable
|
|
15951
|
+
style={({ pressed }) => [
|
|
15952
|
+
styles.button,
|
|
15953
|
+
(!emailAddress || !password || fetchStatus === "fetching") && styles.buttonDisabled,
|
|
15954
|
+
pressed && styles.buttonPressed,
|
|
15955
|
+
]}
|
|
15956
|
+
onPress={handleSubmit}
|
|
15957
|
+
disabled={!emailAddress || !password || fetchStatus === "fetching"}
|
|
15958
|
+
>
|
|
15959
|
+
<Text style={styles.buttonText}>Sign up</Text>
|
|
15960
|
+
</Pressable>
|
|
15961
|
+
<View style={styles.linkContainer}>
|
|
15962
|
+
<Text>Already have an account? </Text>
|
|
15963
|
+
<Link href="/sign-in">
|
|
15964
|
+
<Text style={styles.linkText}>Sign in</Text>
|
|
15965
|
+
</Link>
|
|
15966
|
+
</View>
|
|
15967
|
+
<View nativeID="clerk-captcha" />
|
|
15968
|
+
</View>
|
|
15969
|
+
);
|
|
15970
|
+
}
|
|
15971
|
+
|
|
15972
|
+
const styles = StyleSheet.create({
|
|
15973
|
+
container: {
|
|
15974
|
+
flex: 1,
|
|
15975
|
+
padding: 20,
|
|
15976
|
+
gap: 12,
|
|
15977
|
+
},
|
|
15978
|
+
title: {
|
|
15979
|
+
marginBottom: 8,
|
|
15980
|
+
fontSize: 24,
|
|
15981
|
+
fontWeight: "700",
|
|
15982
|
+
},
|
|
15983
|
+
label: {
|
|
15984
|
+
fontWeight: "600",
|
|
15985
|
+
fontSize: 14,
|
|
15986
|
+
},
|
|
15987
|
+
input: {
|
|
15988
|
+
borderWidth: 1,
|
|
15989
|
+
borderColor: "#ccc",
|
|
15990
|
+
borderRadius: 8,
|
|
15991
|
+
padding: 12,
|
|
15992
|
+
fontSize: 16,
|
|
15993
|
+
backgroundColor: "#fff",
|
|
15994
|
+
},
|
|
15995
|
+
button: {
|
|
15996
|
+
backgroundColor: "#0a7ea4",
|
|
15997
|
+
paddingVertical: 12,
|
|
15998
|
+
paddingHorizontal: 24,
|
|
15999
|
+
borderRadius: 8,
|
|
16000
|
+
alignItems: "center",
|
|
16001
|
+
marginTop: 8,
|
|
16002
|
+
},
|
|
16003
|
+
buttonPressed: {
|
|
16004
|
+
opacity: 0.7,
|
|
16005
|
+
},
|
|
16006
|
+
buttonDisabled: {
|
|
16007
|
+
opacity: 0.5,
|
|
16008
|
+
},
|
|
16009
|
+
buttonText: {
|
|
16010
|
+
color: "#fff",
|
|
16011
|
+
fontWeight: "600",
|
|
16012
|
+
},
|
|
16013
|
+
secondaryButton: {
|
|
16014
|
+
paddingVertical: 12,
|
|
16015
|
+
paddingHorizontal: 24,
|
|
16016
|
+
borderRadius: 8,
|
|
16017
|
+
alignItems: "center",
|
|
16018
|
+
marginTop: 8,
|
|
16019
|
+
},
|
|
16020
|
+
secondaryButtonText: {
|
|
16021
|
+
color: "#0a7ea4",
|
|
16022
|
+
fontWeight: "600",
|
|
16023
|
+
},
|
|
16024
|
+
linkContainer: {
|
|
16025
|
+
flexDirection: "row",
|
|
16026
|
+
gap: 4,
|
|
16027
|
+
marginTop: 12,
|
|
16028
|
+
alignItems: "center",
|
|
16029
|
+
},
|
|
16030
|
+
linkText: {
|
|
16031
|
+
color: "#0a7ea4",
|
|
16032
|
+
fontWeight: "600",
|
|
16033
|
+
},
|
|
16034
|
+
error: {
|
|
16035
|
+
color: "#d32f2f",
|
|
16036
|
+
fontSize: 12,
|
|
16037
|
+
marginTop: -8,
|
|
16038
|
+
},
|
|
16039
|
+
helper: {
|
|
16040
|
+
color: "#555555",
|
|
16041
|
+
fontSize: 13,
|
|
16042
|
+
},
|
|
16043
|
+
});
|
|
16044
|
+
`],
|
|
16045
|
+
["auth/clerk/native/base/components/sign-out-button.tsx.hbs", `import { useClerk } from "@clerk/expo";
|
|
16046
|
+
import { useRouter } from "expo-router";
|
|
16047
|
+
import { Text, TouchableOpacity } from "react-native";
|
|
16048
|
+
|
|
16049
|
+
export const SignOutButton = () => {
|
|
16050
|
+
const { signOut } = useClerk();
|
|
16051
|
+
const router = useRouter();
|
|
16052
|
+
|
|
16053
|
+
const handleSignOut = async () => {
|
|
16054
|
+
try {
|
|
16055
|
+
await signOut();
|
|
16056
|
+
router.replace("/");
|
|
16057
|
+
} catch (err) {
|
|
16058
|
+
console.error(JSON.stringify(err, null, 2));
|
|
16059
|
+
}
|
|
16060
|
+
};
|
|
16061
|
+
|
|
16062
|
+
return (
|
|
16063
|
+
<TouchableOpacity onPress={handleSignOut}>
|
|
16064
|
+
<Text>Sign out</Text>
|
|
16065
|
+
</TouchableOpacity>
|
|
16066
|
+
);
|
|
16067
|
+
};
|
|
16068
|
+
`],
|
|
16069
|
+
["auth/clerk/native/base/utils/clerk-auth.ts.hbs", `type ClerkTokenGetter = () => Promise<string | null>;
|
|
16070
|
+
|
|
16071
|
+
let clerkTokenGetter: ClerkTokenGetter | null = null;
|
|
16072
|
+
|
|
16073
|
+
export function setClerkAuthTokenGetter(getToken: ClerkTokenGetter | null) {
|
|
16074
|
+
clerkTokenGetter = getToken;
|
|
16075
|
+
}
|
|
16076
|
+
|
|
16077
|
+
export async function getClerkAuthToken() {
|
|
16078
|
+
return (await clerkTokenGetter?.()) ?? null;
|
|
16079
|
+
}
|
|
16080
|
+
`],
|
|
16081
|
+
["auth/clerk/web/react/base/src/utils/clerk-auth.ts.hbs", `type ClerkTokenGetter = () => Promise<string | null>;
|
|
16082
|
+
|
|
16083
|
+
let clerkTokenGetter: ClerkTokenGetter | null = null;
|
|
16084
|
+
|
|
16085
|
+
export function setClerkAuthTokenGetter(getToken: ClerkTokenGetter | null) {
|
|
16086
|
+
clerkTokenGetter = getToken;
|
|
16087
|
+
}
|
|
16088
|
+
|
|
16089
|
+
export async function getClerkAuthToken() {
|
|
16090
|
+
return (await clerkTokenGetter?.()) ?? null;
|
|
16091
|
+
}
|
|
16092
|
+
`],
|
|
16093
|
+
["auth/clerk/web/react/next/src/app/dashboard/page.tsx.hbs", `"use client";
|
|
16094
|
+
|
|
16095
|
+
{{#if (eq api "orpc")}}
|
|
16096
|
+
import { useQuery } from "@tanstack/react-query";
|
|
16097
|
+
import { orpc } from "@/utils/orpc";
|
|
16098
|
+
{{/if}}
|
|
16099
|
+
{{#if (eq api "trpc")}}
|
|
16100
|
+
import { useQuery } from "@tanstack/react-query";
|
|
16101
|
+
import { trpc } from "@/utils/trpc";
|
|
16102
|
+
{{/if}}
|
|
16103
|
+
import { SignInButton, UserButton, useUser } from "@clerk/nextjs";
|
|
16104
|
+
|
|
16105
|
+
export default function Dashboard() {
|
|
16106
|
+
const user = useUser();
|
|
16107
|
+
const nameFromParts = [user.user?.firstName, user.user?.lastName].filter(Boolean).join(" ");
|
|
16108
|
+
const displayName =
|
|
16109
|
+
user.user?.fullName ||
|
|
16110
|
+
nameFromParts ||
|
|
16111
|
+
user.user?.username ||
|
|
16112
|
+
user.user?.primaryEmailAddress?.emailAddress ||
|
|
16113
|
+
user.user?.primaryPhoneNumber?.phoneNumber ||
|
|
16114
|
+
"User";
|
|
16115
|
+
{{#if (eq api "orpc")}}
|
|
16116
|
+
const privateData = useQuery({
|
|
16117
|
+
...orpc.privateData.queryOptions(),
|
|
16118
|
+
enabled: user.isLoaded && !!user.user,
|
|
16119
|
+
});
|
|
16120
|
+
{{/if}}
|
|
16121
|
+
{{#if (eq api "trpc")}}
|
|
16122
|
+
const privateData = useQuery({
|
|
16123
|
+
...trpc.privateData.queryOptions(),
|
|
16124
|
+
enabled: user.isLoaded && !!user.user,
|
|
16125
|
+
});
|
|
16126
|
+
{{/if}}
|
|
16127
|
+
|
|
16128
|
+
if (!user.isLoaded) {
|
|
16129
|
+
return <div className="p-6">Loading...</div>;
|
|
16130
|
+
}
|
|
14676
16131
|
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
|
|
16132
|
+
if (!user.user) {
|
|
16133
|
+
return (
|
|
16134
|
+
<div className="p-6">
|
|
16135
|
+
<SignInButton />
|
|
16136
|
+
</div>
|
|
16137
|
+
);
|
|
16138
|
+
}
|
|
14680
16139
|
|
|
14681
16140
|
return (
|
|
14682
|
-
|
|
14683
|
-
<
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
|
|
14688
|
-
|
|
14689
|
-
|
|
14690
|
-
</Authenticated>
|
|
14691
|
-
<Unauthenticated>
|
|
14692
|
-
<SignInButton />
|
|
14693
|
-
</Unauthenticated>
|
|
14694
|
-
<AuthLoading>
|
|
14695
|
-
<div>Loading...</div>
|
|
14696
|
-
</AuthLoading>
|
|
14697
|
-
</>
|
|
16141
|
+
<div className="space-y-4 p-6">
|
|
16142
|
+
<h1 className="text-2xl font-semibold">Dashboard</h1>
|
|
16143
|
+
<p>Welcome {displayName}</p>
|
|
16144
|
+
{{#if (or (eq api "orpc") (eq api "trpc"))}}
|
|
16145
|
+
<p>API: {privateData.data?.message}</p>
|
|
16146
|
+
{{/if}}
|
|
16147
|
+
<UserButton />
|
|
16148
|
+
</div>
|
|
14698
16149
|
);
|
|
14699
16150
|
}
|
|
14700
16151
|
`],
|
|
14701
|
-
["auth/clerk/
|
|
16152
|
+
["auth/clerk/web/react/next/src/proxy.ts.hbs", `import { clerkMiddleware } from "@clerk/nextjs/server";
|
|
14702
16153
|
|
|
14703
16154
|
export default clerkMiddleware();
|
|
14704
16155
|
|
|
@@ -14711,123 +16162,197 @@ export const config = {
|
|
|
14711
16162
|
],
|
|
14712
16163
|
};
|
|
14713
16164
|
`],
|
|
14714
|
-
["auth/clerk/
|
|
14715
|
-
import {
|
|
14716
|
-
import {
|
|
14717
|
-
|
|
14718
|
-
|
|
14719
|
-
|
|
14720
|
-
|
|
14721
|
-
}
|
|
16165
|
+
["auth/clerk/web/react/react-router/src/routes/dashboard.tsx.hbs", `{{#if (eq api "orpc")}}
|
|
16166
|
+
import { useQuery } from "@tanstack/react-query";
|
|
16167
|
+
import { orpc } from "@/utils/orpc";
|
|
16168
|
+
{{/if}}
|
|
16169
|
+
{{#if (eq api "trpc")}}
|
|
16170
|
+
import { useQuery } from "@tanstack/react-query";
|
|
16171
|
+
import { trpc } from "@/utils/trpc";
|
|
16172
|
+
{{/if}}
|
|
16173
|
+
import { SignInButton, UserButton, useUser } from "@clerk/react-router";
|
|
14722
16174
|
|
|
14723
16175
|
export default function Dashboard() {
|
|
14724
|
-
|
|
14725
|
-
|
|
16176
|
+
const user = useUser();
|
|
16177
|
+
const nameFromParts = [user.user?.firstName, user.user?.lastName].filter(Boolean).join(" ");
|
|
16178
|
+
const displayName =
|
|
16179
|
+
user.user?.fullName ||
|
|
16180
|
+
nameFromParts ||
|
|
16181
|
+
user.user?.username ||
|
|
16182
|
+
user.user?.primaryEmailAddress?.emailAddress ||
|
|
16183
|
+
user.user?.primaryPhoneNumber?.phoneNumber ||
|
|
16184
|
+
"User";
|
|
16185
|
+
{{#if (eq api "orpc")}}
|
|
16186
|
+
const privateData = useQuery({
|
|
16187
|
+
...orpc.privateData.queryOptions(),
|
|
16188
|
+
enabled: user.isLoaded && !!user.user,
|
|
16189
|
+
});
|
|
16190
|
+
{{/if}}
|
|
16191
|
+
{{#if (eq api "trpc")}}
|
|
16192
|
+
const privateData = useQuery({
|
|
16193
|
+
...trpc.privateData.queryOptions(),
|
|
16194
|
+
enabled: user.isLoaded && !!user.user,
|
|
16195
|
+
});
|
|
16196
|
+
{{/if}}
|
|
14726
16197
|
|
|
14727
|
-
|
|
14728
|
-
|
|
14729
|
-
|
|
14730
|
-
|
|
14731
|
-
|
|
14732
|
-
|
|
14733
|
-
|
|
14734
|
-
|
|
14735
|
-
|
|
14736
|
-
|
|
14737
|
-
|
|
14738
|
-
|
|
14739
|
-
|
|
14740
|
-
|
|
14741
|
-
|
|
14742
|
-
|
|
14743
|
-
|
|
14744
|
-
|
|
16198
|
+
if (!user.isLoaded) {
|
|
16199
|
+
return <div className="p-6">Loading...</div>;
|
|
16200
|
+
}
|
|
16201
|
+
|
|
16202
|
+
if (!user.user) {
|
|
16203
|
+
return (
|
|
16204
|
+
<div className="p-6">
|
|
16205
|
+
<SignInButton />
|
|
16206
|
+
</div>
|
|
16207
|
+
);
|
|
16208
|
+
}
|
|
16209
|
+
|
|
16210
|
+
return (
|
|
16211
|
+
<div className="space-y-4 p-6">
|
|
16212
|
+
<h1 className="text-2xl font-semibold">Dashboard</h1>
|
|
16213
|
+
<p>Welcome {displayName}</p>
|
|
16214
|
+
{{#if (or (eq api "orpc") (eq api "trpc"))}}
|
|
16215
|
+
<p>API: {privateData.data?.message}</p>
|
|
16216
|
+
{{/if}}
|
|
16217
|
+
<UserButton />
|
|
16218
|
+
</div>
|
|
16219
|
+
);
|
|
14745
16220
|
}
|
|
14746
16221
|
`],
|
|
14747
|
-
["auth/clerk/
|
|
14748
|
-
import {
|
|
16222
|
+
["auth/clerk/web/react/tanstack-router/src/routes/dashboard.tsx.hbs", `{{#if (eq api "orpc")}}
|
|
16223
|
+
import { useQuery } from "@tanstack/react-query";
|
|
16224
|
+
import { orpc } from "@/utils/orpc";
|
|
16225
|
+
{{/if}}
|
|
16226
|
+
{{#if (eq api "trpc")}}
|
|
16227
|
+
import { useQuery } from "@tanstack/react-query";
|
|
16228
|
+
import { trpc } from "@/utils/trpc";
|
|
16229
|
+
{{/if}}
|
|
16230
|
+
import { SignInButton, UserButton, useUser } from "@clerk/react";
|
|
14749
16231
|
import { createFileRoute } from "@tanstack/react-router";
|
|
14750
|
-
import {
|
|
14751
|
-
Authenticated,
|
|
14752
|
-
AuthLoading,
|
|
14753
|
-
Unauthenticated,
|
|
14754
|
-
useQuery,
|
|
14755
|
-
} from "convex/react";
|
|
14756
16232
|
|
|
14757
16233
|
export const Route = createFileRoute("/dashboard")({
|
|
14758
16234
|
component: RouteComponent,
|
|
14759
16235
|
});
|
|
14760
16236
|
|
|
14761
16237
|
function RouteComponent() {
|
|
14762
|
-
const
|
|
14763
|
-
const
|
|
16238
|
+
const user = useUser();
|
|
16239
|
+
const nameFromParts = [user.user?.firstName, user.user?.lastName].filter(Boolean).join(" ");
|
|
16240
|
+
const displayName =
|
|
16241
|
+
user.user?.fullName ||
|
|
16242
|
+
nameFromParts ||
|
|
16243
|
+
user.user?.username ||
|
|
16244
|
+
user.user?.primaryEmailAddress?.emailAddress ||
|
|
16245
|
+
user.user?.primaryPhoneNumber?.phoneNumber ||
|
|
16246
|
+
"User";
|
|
16247
|
+
{{#if (eq api "orpc")}}
|
|
16248
|
+
const privateData = useQuery({
|
|
16249
|
+
...orpc.privateData.queryOptions(),
|
|
16250
|
+
enabled: user.isLoaded && !!user.user,
|
|
16251
|
+
});
|
|
16252
|
+
{{/if}}
|
|
16253
|
+
{{#if (eq api "trpc")}}
|
|
16254
|
+
const privateData = useQuery({
|
|
16255
|
+
...trpc.privateData.queryOptions(),
|
|
16256
|
+
enabled: user.isLoaded && !!user.user,
|
|
16257
|
+
});
|
|
16258
|
+
{{/if}}
|
|
14764
16259
|
|
|
14765
|
-
|
|
14766
|
-
|
|
14767
|
-
|
|
14768
|
-
|
|
14769
|
-
|
|
14770
|
-
|
|
14771
|
-
|
|
14772
|
-
<UserButton />
|
|
14773
|
-
</div>
|
|
14774
|
-
</Authenticated>
|
|
14775
|
-
<Unauthenticated>
|
|
16260
|
+
if (!user.isLoaded) {
|
|
16261
|
+
return <div className="p-6">Loading...</div>;
|
|
16262
|
+
}
|
|
16263
|
+
|
|
16264
|
+
if (!user.user) {
|
|
16265
|
+
return (
|
|
16266
|
+
<div className="p-6">
|
|
14776
16267
|
<SignInButton />
|
|
14777
|
-
</
|
|
14778
|
-
|
|
14779
|
-
|
|
14780
|
-
|
|
14781
|
-
|
|
16268
|
+
</div>
|
|
16269
|
+
);
|
|
16270
|
+
}
|
|
16271
|
+
|
|
16272
|
+
return (
|
|
16273
|
+
<div className="space-y-4 p-6">
|
|
16274
|
+
<h1 className="text-2xl font-semibold">Dashboard</h1>
|
|
16275
|
+
<p>Welcome {displayName}</p>
|
|
16276
|
+
{{#if (or (eq api "orpc") (eq api "trpc"))}}
|
|
16277
|
+
<p>API: {privateData.data?.message}</p>
|
|
16278
|
+
{{/if}}
|
|
16279
|
+
<UserButton />
|
|
16280
|
+
</div>
|
|
14782
16281
|
);
|
|
14783
16282
|
}
|
|
14784
16283
|
`],
|
|
14785
|
-
["auth/clerk/
|
|
14786
|
-
import {
|
|
16284
|
+
["auth/clerk/web/react/tanstack-start/src/routes/dashboard.tsx.hbs", `{{#if (eq api "trpc")}}
|
|
16285
|
+
import { useTRPC } from "@/utils/trpc";
|
|
16286
|
+
import { useQuery } from "@tanstack/react-query";
|
|
16287
|
+
{{/if}}
|
|
16288
|
+
{{#if (eq api "orpc")}}
|
|
16289
|
+
import { useQuery } from "@tanstack/react-query";
|
|
16290
|
+
import { orpc } from "@/utils/orpc";
|
|
16291
|
+
{{/if}}
|
|
16292
|
+
import { SignInButton, UserButton, useUser } from "@clerk/tanstack-react-start";
|
|
14787
16293
|
import { createFileRoute } from "@tanstack/react-router";
|
|
14788
|
-
import {
|
|
14789
|
-
Authenticated,
|
|
14790
|
-
AuthLoading,
|
|
14791
|
-
Unauthenticated,
|
|
14792
|
-
useQuery,
|
|
14793
|
-
} from "convex/react";
|
|
14794
16294
|
|
|
14795
16295
|
export const Route = createFileRoute("/dashboard")({
|
|
14796
16296
|
component: RouteComponent,
|
|
14797
16297
|
});
|
|
14798
16298
|
|
|
14799
16299
|
function RouteComponent() {
|
|
14800
|
-
const privateData = useQuery(api.privateData.get);
|
|
14801
16300
|
const user = useUser();
|
|
16301
|
+
const nameFromParts = [user.user?.firstName, user.user?.lastName].filter(Boolean).join(" ");
|
|
16302
|
+
const displayName =
|
|
16303
|
+
user.user?.fullName ||
|
|
16304
|
+
nameFromParts ||
|
|
16305
|
+
user.user?.username ||
|
|
16306
|
+
user.user?.primaryEmailAddress?.emailAddress ||
|
|
16307
|
+
user.user?.primaryPhoneNumber?.phoneNumber ||
|
|
16308
|
+
"User";
|
|
16309
|
+
{{#if (eq api "trpc")}}
|
|
16310
|
+
const trpc = useTRPC();
|
|
16311
|
+
const privateData = useQuery({
|
|
16312
|
+
...trpc.privateData.queryOptions(),
|
|
16313
|
+
enabled: user.isLoaded && !!user.user,
|
|
16314
|
+
});
|
|
16315
|
+
{{/if}}
|
|
16316
|
+
{{#if (eq api "orpc")}}
|
|
16317
|
+
const privateData = useQuery({
|
|
16318
|
+
...orpc.privateData.queryOptions(),
|
|
16319
|
+
enabled: user.isLoaded && !!user.user,
|
|
16320
|
+
});
|
|
16321
|
+
{{/if}}
|
|
14802
16322
|
|
|
14803
|
-
|
|
14804
|
-
|
|
14805
|
-
|
|
14806
|
-
|
|
14807
|
-
|
|
14808
|
-
|
|
14809
|
-
|
|
14810
|
-
<UserButton />
|
|
14811
|
-
</div>
|
|
14812
|
-
</Authenticated>
|
|
14813
|
-
<Unauthenticated>
|
|
16323
|
+
if (!user.isLoaded) {
|
|
16324
|
+
return <div className="p-6">Loading...</div>;
|
|
16325
|
+
}
|
|
16326
|
+
|
|
16327
|
+
if (!user.user) {
|
|
16328
|
+
return (
|
|
16329
|
+
<div className="p-6">
|
|
14814
16330
|
<SignInButton />
|
|
14815
|
-
</
|
|
14816
|
-
|
|
14817
|
-
|
|
14818
|
-
|
|
14819
|
-
|
|
16331
|
+
</div>
|
|
16332
|
+
);
|
|
16333
|
+
}
|
|
16334
|
+
|
|
16335
|
+
return (
|
|
16336
|
+
<div className="space-y-4 p-6">
|
|
16337
|
+
<h1 className="text-2xl font-semibold">Dashboard</h1>
|
|
16338
|
+
<p>Welcome {displayName}</p>
|
|
16339
|
+
{{#if (or (eq api "orpc") (eq api "trpc"))}}
|
|
16340
|
+
<p>API: {privateData.data?.message}</p>
|
|
16341
|
+
{{/if}}
|
|
16342
|
+
<UserButton />
|
|
16343
|
+
</div>
|
|
14820
16344
|
);
|
|
14821
16345
|
}
|
|
14822
16346
|
`],
|
|
14823
|
-
["auth/clerk/
|
|
16347
|
+
["auth/clerk/web/react/tanstack-start/src/start.ts.hbs", `import { clerkMiddleware } from '@clerk/tanstack-react-start/server'
|
|
14824
16348
|
import { createStart } from '@tanstack/react-start'
|
|
14825
16349
|
|
|
14826
16350
|
export const startInstance = createStart(() => {
|
|
14827
16351
|
return {
|
|
14828
16352
|
requestMiddleware: [clerkMiddleware()],
|
|
14829
16353
|
}
|
|
14830
|
-
})
|
|
16354
|
+
})
|
|
16355
|
+
`],
|
|
14831
16356
|
["backend/convex/packages/backend/_gitignore", `
|
|
14832
16357
|
.env.local
|
|
14833
16358
|
`],
|
|
@@ -15158,8 +16683,10 @@ const app = new Elysia()
|
|
|
15158
16683
|
cors({
|
|
15159
16684
|
origin: env.CORS_ORIGIN,
|
|
15160
16685
|
methods: ["GET", "POST", "OPTIONS"],
|
|
15161
|
-
{{#if (eq auth "better-auth")}}
|
|
16686
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
15162
16687
|
allowedHeaders: ["Content-Type", "Authorization"],
|
|
16688
|
+
{{/if}}
|
|
16689
|
+
{{#if (eq auth "better-auth")}}
|
|
15163
16690
|
credentials: true,
|
|
15164
16691
|
{{/if}}
|
|
15165
16692
|
}),
|
|
@@ -15246,7 +16773,7 @@ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
|
|
|
15246
16773
|
import { RPCHandler } from "@orpc/server/node";
|
|
15247
16774
|
import { onError } from "@orpc/server";
|
|
15248
16775
|
import { appRouter } from "@{{projectName}}/api/routers/index";
|
|
15249
|
-
{{#if (eq auth "better-auth")}}
|
|
16776
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
15250
16777
|
import { createContext } from "@{{projectName}}/api/context";
|
|
15251
16778
|
{{/if}}
|
|
15252
16779
|
{{/if}}
|
|
@@ -15261,6 +16788,9 @@ import { devToolsMiddleware } from "@ai-sdk/devtools";
|
|
|
15261
16788
|
import { auth } from "@{{projectName}}/auth";
|
|
15262
16789
|
import { toNodeHandler } from "better-auth/node";
|
|
15263
16790
|
{{/if}}
|
|
16791
|
+
{{#if (eq auth "clerk")}}
|
|
16792
|
+
import { clerkMiddleware } from "@clerk/express";
|
|
16793
|
+
{{/if}}
|
|
15264
16794
|
|
|
15265
16795
|
const app = express();
|
|
15266
16796
|
|
|
@@ -15268,13 +16798,19 @@ app.use(
|
|
|
15268
16798
|
cors({
|
|
15269
16799
|
origin: env.CORS_ORIGIN,
|
|
15270
16800
|
methods: ["GET", "POST", "OPTIONS"],
|
|
15271
|
-
{{#if (eq auth "better-auth")}}
|
|
16801
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
15272
16802
|
allowedHeaders: ["Content-Type", "Authorization"],
|
|
16803
|
+
{{/if}}
|
|
16804
|
+
{{#if (eq auth "better-auth")}}
|
|
15273
16805
|
credentials: true,
|
|
15274
16806
|
{{/if}}
|
|
15275
16807
|
})
|
|
15276
16808
|
);
|
|
15277
16809
|
|
|
16810
|
+
{{#if (eq auth "clerk")}}
|
|
16811
|
+
app.use(clerkMiddleware());
|
|
16812
|
+
{{/if}}
|
|
16813
|
+
|
|
15278
16814
|
{{#if (eq auth "better-auth")}}
|
|
15279
16815
|
app.all("/api/auth{/*path}", toNodeHandler(auth));
|
|
15280
16816
|
{{/if}}
|
|
@@ -15313,7 +16849,7 @@ const apiHandler = new OpenAPIHandler(appRouter, {
|
|
|
15313
16849
|
app.use(async (req, res, next) => {
|
|
15314
16850
|
const rpcResult = await rpcHandler.handle(req, res, {
|
|
15315
16851
|
prefix: "/rpc",
|
|
15316
|
-
{{#if (eq auth "better-auth")}}
|
|
16852
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
15317
16853
|
context: await createContext({ req }),
|
|
15318
16854
|
{{else}}
|
|
15319
16855
|
context: {},
|
|
@@ -15323,7 +16859,7 @@ app.use(async (req, res, next) => {
|
|
|
15323
16859
|
|
|
15324
16860
|
const apiResult = await apiHandler.handle(req, res, {
|
|
15325
16861
|
prefix: "/api-reference",
|
|
15326
|
-
{{#if (eq auth "better-auth")}}
|
|
16862
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
15327
16863
|
context: await createContext({ req }),
|
|
15328
16864
|
{{else}}
|
|
15329
16865
|
context: {},
|
|
@@ -15389,6 +16925,9 @@ import { devToolsMiddleware } from "@ai-sdk/devtools";
|
|
|
15389
16925
|
{{#if (eq auth "better-auth")}}
|
|
15390
16926
|
import { auth } from "@{{projectName}}/auth";
|
|
15391
16927
|
{{/if}}
|
|
16928
|
+
{{#if (eq auth "clerk")}}
|
|
16929
|
+
import { clerkPlugin } from "@clerk/fastify";
|
|
16930
|
+
{{/if}}
|
|
15392
16931
|
|
|
15393
16932
|
const baseCorsConfig = {
|
|
15394
16933
|
origin: env.CORS_ORIGIN,
|
|
@@ -15434,6 +16973,9 @@ const fastify = Fastify({
|
|
|
15434
16973
|
{{/if}}
|
|
15435
16974
|
|
|
15436
16975
|
fastify.register(fastifyCors, baseCorsConfig);
|
|
16976
|
+
{{#if (eq auth "clerk")}}
|
|
16977
|
+
fastify.register(clerkPlugin);
|
|
16978
|
+
{{/if}}
|
|
15437
16979
|
|
|
15438
16980
|
{{#if (eq api "orpc")}}
|
|
15439
16981
|
fastify.register(async (rpcApp) => {
|
|
@@ -15444,7 +16986,7 @@ fastify.register(async (rpcApp) => {
|
|
|
15444
16986
|
|
|
15445
16987
|
rpcApp.all("/rpc/*", async (request, reply) => {
|
|
15446
16988
|
const { matched } = await rpcHandler.handle(request, reply, {
|
|
15447
|
-
context: await createContext(request.headers),
|
|
16989
|
+
context: await createContext({{#if (eq auth "clerk")}}request{{else}}request.headers{{/if}}),
|
|
15448
16990
|
prefix: "/rpc",
|
|
15449
16991
|
});
|
|
15450
16992
|
|
|
@@ -15455,7 +16997,7 @@ fastify.register(async (rpcApp) => {
|
|
|
15455
16997
|
|
|
15456
16998
|
rpcApp.all("/api-reference/*", async (request, reply) => {
|
|
15457
16999
|
const { matched } = await apiHandler.handle(request, reply, {
|
|
15458
|
-
context: await createContext(request.headers),
|
|
17000
|
+
context: await createContext({{#if (eq auth "clerk")}}request{{else}}request.headers{{/if}}),
|
|
15459
17001
|
prefix: "/api-reference",
|
|
15460
17002
|
});
|
|
15461
17003
|
|
|
@@ -15583,8 +17125,10 @@ app.use(
|
|
|
15583
17125
|
cors({
|
|
15584
17126
|
origin: env.CORS_ORIGIN,
|
|
15585
17127
|
allowMethods: ["GET", "POST", "OPTIONS"],
|
|
15586
|
-
{{#if (eq auth "better-auth")}}
|
|
17128
|
+
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
15587
17129
|
allowHeaders: ["Content-Type", "Authorization"],
|
|
17130
|
+
{{/if}}
|
|
17131
|
+
{{#if (eq auth "better-auth")}}
|
|
15588
17132
|
credentials: true,
|
|
15589
17133
|
{{/if}}
|
|
15590
17134
|
})
|
|
@@ -21374,7 +22918,7 @@ import {
|
|
|
21374
22918
|
import { Checkbox } from "@{{projectName}}/ui/components/checkbox";
|
|
21375
22919
|
import { Input } from "@{{projectName}}/ui/components/input";
|
|
21376
22920
|
import { Loader2, Trash2 } from "lucide-react";
|
|
21377
|
-
import { useState } from "react";
|
|
22921
|
+
import { useState, type FormEvent } from "react";
|
|
21378
22922
|
|
|
21379
22923
|
{{#if (eq backend "convex")}}
|
|
21380
22924
|
import { useMutation, useQuery } from "convex/react";
|
|
@@ -21400,7 +22944,7 @@ export default function TodosPage() {
|
|
|
21400
22944
|
const toggleTodoMutation = useMutation(api.todos.toggle);
|
|
21401
22945
|
const deleteTodoMutation = useMutation(api.todos.deleteTodo);
|
|
21402
22946
|
|
|
21403
|
-
const handleAddTodo = async (e) => {
|
|
22947
|
+
const handleAddTodo = async (e: FormEvent<HTMLFormElement>) => {
|
|
21404
22948
|
e.preventDefault();
|
|
21405
22949
|
const text = newTodoText.trim();
|
|
21406
22950
|
if (!text) return;
|
|
@@ -21459,7 +23003,7 @@ export default function TodosPage() {
|
|
|
21459
23003
|
);
|
|
21460
23004
|
{{/if}}
|
|
21461
23005
|
|
|
21462
|
-
const handleAddTodo = (e) => {
|
|
23006
|
+
const handleAddTodo = (e: FormEvent<HTMLFormElement>) => {
|
|
21463
23007
|
e.preventDefault();
|
|
21464
23008
|
if (newTodoText.trim()) {
|
|
21465
23009
|
createMutation.mutate({ text: newTodoText });
|
|
@@ -21618,7 +23162,7 @@ import {
|
|
|
21618
23162
|
import { Checkbox } from "@{{projectName}}/ui/components/checkbox";
|
|
21619
23163
|
import { Input } from "@{{projectName}}/ui/components/input";
|
|
21620
23164
|
import { Loader2, Trash2 } from "lucide-react";
|
|
21621
|
-
import { useState } from "react";
|
|
23165
|
+
import { useState, type FormEvent } from "react";
|
|
21622
23166
|
|
|
21623
23167
|
{{#if (eq backend "convex")}}
|
|
21624
23168
|
import { useMutation, useQuery } from "convex/react";
|
|
@@ -21643,7 +23187,7 @@ export default function Todos() {
|
|
|
21643
23187
|
const toggleTodo = useMutation(api.todos.toggle);
|
|
21644
23188
|
const deleteTodo = useMutation(api.todos.deleteTodo);
|
|
21645
23189
|
|
|
21646
|
-
const handleAddTodo = async (e) => {
|
|
23190
|
+
const handleAddTodo = async (e: FormEvent<HTMLFormElement>) => {
|
|
21647
23191
|
e.preventDefault();
|
|
21648
23192
|
const text = newTodoText.trim();
|
|
21649
23193
|
if (!text) return;
|
|
@@ -21702,7 +23246,7 @@ export default function Todos() {
|
|
|
21702
23246
|
);
|
|
21703
23247
|
{{/if}}
|
|
21704
23248
|
|
|
21705
|
-
const handleAddTodo = (e) => {
|
|
23249
|
+
const handleAddTodo = (e: FormEvent<HTMLFormElement>) => {
|
|
21706
23250
|
e.preventDefault();
|
|
21707
23251
|
if (newTodoText.trim()) {
|
|
21708
23252
|
createMutation.mutate({ text: newTodoText });
|
|
@@ -21862,7 +23406,7 @@ import { Checkbox } from "@{{projectName}}/ui/components/checkbox";
|
|
|
21862
23406
|
import { Input } from "@{{projectName}}/ui/components/input";
|
|
21863
23407
|
import { createFileRoute } from "@tanstack/react-router";
|
|
21864
23408
|
import { Loader2, Trash2 } from "lucide-react";
|
|
21865
|
-
import { useState } from "react";
|
|
23409
|
+
import { useState, type FormEvent } from "react";
|
|
21866
23410
|
|
|
21867
23411
|
{{#if (eq backend "convex")}}
|
|
21868
23412
|
import { useMutation, useQuery } from "convex/react";
|
|
@@ -21891,7 +23435,7 @@ function TodosRoute() {
|
|
|
21891
23435
|
const toggleTodo = useMutation(api.todos.toggle);
|
|
21892
23436
|
const deleteTodo = useMutation(api.todos.deleteTodo);
|
|
21893
23437
|
|
|
21894
|
-
const handleAddTodo = async (e) => {
|
|
23438
|
+
const handleAddTodo = async (e: FormEvent<HTMLFormElement>) => {
|
|
21895
23439
|
e.preventDefault();
|
|
21896
23440
|
const text = newTodoText.trim();
|
|
21897
23441
|
if (!text) return;
|
|
@@ -21950,7 +23494,7 @@ function TodosRoute() {
|
|
|
21950
23494
|
);
|
|
21951
23495
|
{{/if}}
|
|
21952
23496
|
|
|
21953
|
-
const handleAddTodo = (e) => {
|
|
23497
|
+
const handleAddTodo = (e: FormEvent<HTMLFormElement>) => {
|
|
21954
23498
|
e.preventDefault();
|
|
21955
23499
|
if (newTodoText.trim()) {
|
|
21956
23500
|
createMutation.mutate({ text: newTodoText });
|
|
@@ -22114,7 +23658,7 @@ import { Trash2 } from "lucide-react";
|
|
|
22114
23658
|
{{else}}
|
|
22115
23659
|
import { Loader2, Trash2 } from "lucide-react";
|
|
22116
23660
|
{{/if}}
|
|
22117
|
-
import { useState } from "react";
|
|
23661
|
+
import { useState, type FormEvent } from "react";
|
|
22118
23662
|
|
|
22119
23663
|
{{#if (eq backend "convex")}}
|
|
22120
23664
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
@@ -22147,7 +23691,7 @@ function TodosRoute() {
|
|
|
22147
23691
|
const toggleTodo = useMutation(api.todos.toggle);
|
|
22148
23692
|
const removeTodo = useMutation(api.todos.deleteTodo);
|
|
22149
23693
|
|
|
22150
|
-
const handleAddTodo = async (e) => {
|
|
23694
|
+
const handleAddTodo = async (e: FormEvent<HTMLFormElement>) => {
|
|
22151
23695
|
e.preventDefault();
|
|
22152
23696
|
const text = newTodoText.trim();
|
|
22153
23697
|
if (text) {
|
|
@@ -22226,7 +23770,7 @@ function TodosRoute() {
|
|
|
22226
23770
|
);
|
|
22227
23771
|
{{/if}}
|
|
22228
23772
|
|
|
22229
|
-
const handleAddTodo = (e) => {
|
|
23773
|
+
const handleAddTodo = (e: FormEvent<HTMLFormElement>) => {
|
|
22230
23774
|
e.preventDefault();
|
|
22231
23775
|
if (newTodoText.trim()) {
|
|
22232
23776
|
createMutation.mutate({ text: newTodoText });
|
|
@@ -23193,6 +24737,15 @@ web-build/
|
|
|
23193
24737
|
["frontend/native/bare/app/_layout.tsx.hbs", `{{#if (includes examples "ai")}}
|
|
23194
24738
|
import "@/polyfills";
|
|
23195
24739
|
{{/if}}
|
|
24740
|
+
{{#if (and (eq auth "clerk") (ne api "none") (ne backend "convex"))}}
|
|
24741
|
+
import { useEffect } from "react";
|
|
24742
|
+
import { setClerkAuthTokenGetter } from "@/utils/clerk-auth";
|
|
24743
|
+
{{/if}}
|
|
24744
|
+
{{#if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
24745
|
+
import { ClerkProvider{{#unless (eq api "none")}}, useAuth{{/unless}} } from "@clerk/expo";
|
|
24746
|
+
import { tokenCache } from "@clerk/expo/token-cache";
|
|
24747
|
+
import { env } from "@{{projectName}}/env/native";
|
|
24748
|
+
{{/if}}
|
|
23196
24749
|
|
|
23197
24750
|
{{#if (eq backend "convex")}}
|
|
23198
24751
|
{{#if (eq auth "better-auth")}}
|
|
@@ -23205,9 +24758,9 @@ import "@/polyfills";
|
|
|
23205
24758
|
import { env } from "@{{projectName}}/env/native";
|
|
23206
24759
|
{{/if}}
|
|
23207
24760
|
{{#if (eq auth "clerk")}}
|
|
23208
|
-
import { ClerkProvider, useAuth } from "@clerk/
|
|
24761
|
+
import { ClerkProvider, useAuth } from "@clerk/expo";
|
|
23209
24762
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
23210
|
-
import { tokenCache } from "@clerk/
|
|
24763
|
+
import { tokenCache } from "@clerk/expo/token-cache";
|
|
23211
24764
|
{{/if}}
|
|
23212
24765
|
{{else}}
|
|
23213
24766
|
{{#unless (eq api "none")}}
|
|
@@ -23259,6 +24812,22 @@ const styles = StyleSheet.create({
|
|
|
23259
24812
|
},
|
|
23260
24813
|
});
|
|
23261
24814
|
|
|
24815
|
+
{{#if (and (eq auth "clerk") (ne api "none") (ne backend "convex"))}}
|
|
24816
|
+
function ClerkApiAuthBridge() {
|
|
24817
|
+
const { getToken } = useAuth();
|
|
24818
|
+
|
|
24819
|
+
useEffect(() => {
|
|
24820
|
+
setClerkAuthTokenGetter(getToken);
|
|
24821
|
+
|
|
24822
|
+
return () => {
|
|
24823
|
+
setClerkAuthTokenGetter(null);
|
|
24824
|
+
};
|
|
24825
|
+
}, [getToken]);
|
|
24826
|
+
|
|
24827
|
+
return null;
|
|
24828
|
+
}
|
|
24829
|
+
{{/if}}
|
|
24830
|
+
|
|
23262
24831
|
export default function RootLayout() {
|
|
23263
24832
|
const { isDarkColorScheme } = useColorScheme();
|
|
23264
24833
|
|
|
@@ -23303,32 +24872,63 @@ export default function RootLayout() {
|
|
|
23303
24872
|
</Stack>
|
|
23304
24873
|
</GestureHandlerRootView>
|
|
23305
24874
|
</ThemeProvider>
|
|
23306
|
-
</ConvexProvider>
|
|
24875
|
+
</ConvexProvider>
|
|
24876
|
+
{{/if}}
|
|
24877
|
+
{{else}}
|
|
24878
|
+
{{#if (eq auth "clerk")}}
|
|
24879
|
+
<ClerkProvider tokenCache={tokenCache} publishableKey={env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY}>
|
|
24880
|
+
{{#unless (eq api "none")}}
|
|
24881
|
+
<ClerkApiAuthBridge />
|
|
24882
|
+
<QueryClientProvider client={queryClient}>
|
|
24883
|
+
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
|
24884
|
+
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
|
|
24885
|
+
<GestureHandlerRootView style={styles.container}>
|
|
24886
|
+
<Stack>
|
|
24887
|
+
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
24888
|
+
<Stack.Screen name="(auth)" options=\\{{ headerShown: false }} />
|
|
24889
|
+
<Stack.Screen name="modal" options=\\{{ title: "Modal", presentation: "modal" }} />
|
|
24890
|
+
</Stack>
|
|
24891
|
+
</GestureHandlerRootView>
|
|
24892
|
+
</ThemeProvider>
|
|
24893
|
+
</QueryClientProvider>
|
|
24894
|
+
{{else}}
|
|
24895
|
+
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
|
24896
|
+
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
|
|
24897
|
+
<GestureHandlerRootView style={styles.container}>
|
|
24898
|
+
<Stack>
|
|
24899
|
+
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
24900
|
+
<Stack.Screen name="(auth)" options=\\{{ headerShown: false }} />
|
|
24901
|
+
<Stack.Screen name="modal" options=\\{{ title: "Modal", presentation: "modal" }} />
|
|
24902
|
+
</Stack>
|
|
24903
|
+
</GestureHandlerRootView>
|
|
24904
|
+
</ThemeProvider>
|
|
24905
|
+
{{/unless}}
|
|
24906
|
+
</ClerkProvider>
|
|
24907
|
+
{{else}}
|
|
24908
|
+
{{#unless (eq api "none")}}
|
|
24909
|
+
<QueryClientProvider client={queryClient}>
|
|
24910
|
+
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
|
24911
|
+
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
|
|
24912
|
+
<GestureHandlerRootView style={styles.container}>
|
|
24913
|
+
<Stack>
|
|
24914
|
+
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
24915
|
+
<Stack.Screen name="modal" options=\\{{ title: "Modal", presentation: "modal" }} />
|
|
24916
|
+
</Stack>
|
|
24917
|
+
</GestureHandlerRootView>
|
|
24918
|
+
</ThemeProvider>
|
|
24919
|
+
</QueryClientProvider>
|
|
24920
|
+
{{else}}
|
|
24921
|
+
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
|
24922
|
+
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
|
|
24923
|
+
<GestureHandlerRootView style={styles.container}>
|
|
24924
|
+
<Stack>
|
|
24925
|
+
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
24926
|
+
<Stack.Screen name="modal" options=\\{{ title: "Modal", presentation: "modal" }} />
|
|
24927
|
+
</Stack>
|
|
24928
|
+
</GestureHandlerRootView>
|
|
24929
|
+
</ThemeProvider>
|
|
24930
|
+
{{/unless}}
|
|
23307
24931
|
{{/if}}
|
|
23308
|
-
{{else}}
|
|
23309
|
-
{{#unless (eq api "none")}}
|
|
23310
|
-
<QueryClientProvider client={queryClient}>
|
|
23311
|
-
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
|
23312
|
-
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
|
|
23313
|
-
<GestureHandlerRootView style={styles.container}>
|
|
23314
|
-
<Stack>
|
|
23315
|
-
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
23316
|
-
<Stack.Screen name="modal" options=\\{{ title: "Modal", presentation: "modal" }} />
|
|
23317
|
-
</Stack>
|
|
23318
|
-
</GestureHandlerRootView>
|
|
23319
|
-
</ThemeProvider>
|
|
23320
|
-
</QueryClientProvider>
|
|
23321
|
-
{{else}}
|
|
23322
|
-
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
|
23323
|
-
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
|
|
23324
|
-
<GestureHandlerRootView style={styles.container}>
|
|
23325
|
-
<Stack>
|
|
23326
|
-
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
23327
|
-
<Stack.Screen name="modal" options=\\{{ title: "Modal", presentation: "modal" }} />
|
|
23328
|
-
</Stack>
|
|
23329
|
-
</GestureHandlerRootView>
|
|
23330
|
-
</ThemeProvider>
|
|
23331
|
-
{{/unless}}
|
|
23332
24932
|
{{/if}}
|
|
23333
24933
|
</>
|
|
23334
24934
|
);
|
|
@@ -23571,7 +25171,11 @@ import { trpc } from "@/utils/trpc";
|
|
|
23571
25171
|
import { Link } from "expo-router";
|
|
23572
25172
|
import { Authenticated, AuthLoading, Unauthenticated, useQuery } from "convex/react";
|
|
23573
25173
|
import { api } from "@{{ projectName }}/backend/convex/_generated/api";
|
|
23574
|
-
import { useUser } from "@clerk/
|
|
25174
|
+
import { useUser } from "@clerk/expo";
|
|
25175
|
+
import { SignOutButton } from "@/components/sign-out-button";
|
|
25176
|
+
{{else if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
25177
|
+
import { Link } from "expo-router";
|
|
25178
|
+
import { useAuth, useUser } from "@clerk/expo";
|
|
23575
25179
|
import { SignOutButton } from "@/components/sign-out-button";
|
|
23576
25180
|
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
23577
25181
|
import { useConvexAuth, useQuery } from "convex/react";
|
|
@@ -23597,6 +25201,9 @@ const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
|
23597
25201
|
const { user } = useUser();
|
|
23598
25202
|
const healthCheck = useQuery(api.healthCheck.get);
|
|
23599
25203
|
const privateData = useQuery(api.privateData.get);
|
|
25204
|
+
{{else if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
25205
|
+
const { isLoaded, isSignedIn } = useAuth();
|
|
25206
|
+
const { user } = useUser();
|
|
23600
25207
|
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
23601
25208
|
const healthCheck = useQuery(api.healthCheck.get);
|
|
23602
25209
|
const { isAuthenticated } = useConvexAuth();
|
|
@@ -23672,6 +25279,33 @@ return (
|
|
|
23672
25279
|
</AuthLoading>
|
|
23673
25280
|
{{/if}}
|
|
23674
25281
|
|
|
25282
|
+
{{#if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
25283
|
+
{!isLoaded ? (
|
|
25284
|
+
<Text style=\\{{ color: theme.text }}>Loading...</Text>
|
|
25285
|
+
) : isSignedIn ? (
|
|
25286
|
+
<View style={[styles.userCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
|
|
25287
|
+
<View style={styles.userHeader}>
|
|
25288
|
+
<Text style={[styles.userText, { color: theme.text }]}>
|
|
25289
|
+
Welcome, <Text style={styles.userName}>{user?.fullName ?? user?.firstName ?? "there"}</Text>
|
|
25290
|
+
</Text>
|
|
25291
|
+
</View>
|
|
25292
|
+
<Text style={[styles.userEmail, { color: theme.text, opacity: 0.7 }]}>
|
|
25293
|
+
{user?.emailAddresses[0]?.emailAddress}
|
|
25294
|
+
</Text>
|
|
25295
|
+
<SignOutButton />
|
|
25296
|
+
</View>
|
|
25297
|
+
) : (
|
|
25298
|
+
<View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
|
|
25299
|
+
<Link href="/(auth)/sign-in">
|
|
25300
|
+
<Text style=\\{{ color: theme.primary }}>Sign in</Text>
|
|
25301
|
+
</Link>
|
|
25302
|
+
<Link href="/(auth)/sign-up">
|
|
25303
|
+
<Text style=\\{{ color: theme.primary }}>Sign up</Text>
|
|
25304
|
+
</Link>
|
|
25305
|
+
</View>
|
|
25306
|
+
)}
|
|
25307
|
+
{{/if}}
|
|
25308
|
+
|
|
23675
25309
|
{{#if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
23676
25310
|
{user ? (
|
|
23677
25311
|
<View style={[styles.userCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
|
|
@@ -23788,7 +25422,8 @@ statusCardTitle: {
|
|
|
23788
25422
|
marginBottom: 8,
|
|
23789
25423
|
fontWeight: "bold",
|
|
23790
25424
|
},
|
|
23791
|
-
})
|
|
25425
|
+
});
|
|
25426
|
+
`],
|
|
23792
25427
|
["frontend/native/bare/app/+not-found.tsx.hbs", `import { Container } from "@/components/container";
|
|
23793
25428
|
import { Link, Stack } from "expo-router";
|
|
23794
25429
|
import { Text, View, StyleSheet } from "react-native";
|
|
@@ -24169,6 +25804,15 @@ android
|
|
|
24169
25804
|
["frontend/native/unistyles/app/_layout.tsx.hbs", `{{#if (includes examples "ai")}}
|
|
24170
25805
|
import "@/polyfills";
|
|
24171
25806
|
{{/if}}
|
|
25807
|
+
{{#if (and (eq auth "clerk") (ne api "none") (ne backend "convex"))}}
|
|
25808
|
+
import { useEffect } from "react";
|
|
25809
|
+
import { setClerkAuthTokenGetter } from "@/utils/clerk-auth";
|
|
25810
|
+
{{/if}}
|
|
25811
|
+
{{#if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
25812
|
+
import { ClerkProvider{{#unless (eq api "none")}}, useAuth{{/unless}} } from "@clerk/expo";
|
|
25813
|
+
import { tokenCache } from "@clerk/expo/token-cache";
|
|
25814
|
+
import { env } from "@{{projectName}}/env/native";
|
|
25815
|
+
{{/if}}
|
|
24172
25816
|
{{#if (eq api "trpc")}}
|
|
24173
25817
|
import { queryClient } from "@/utils/trpc";
|
|
24174
25818
|
{{/if}}
|
|
@@ -24186,9 +25830,9 @@ import { ConvexProvider, ConvexReactClient } from "convex/react";
|
|
|
24186
25830
|
import { env } from "@{{projectName}}/env/native";
|
|
24187
25831
|
{{/if}}
|
|
24188
25832
|
{{#if (eq auth "clerk")}}
|
|
24189
|
-
import { ClerkProvider, useAuth } from "@clerk/
|
|
25833
|
+
import { ClerkProvider, useAuth } from "@clerk/expo";
|
|
24190
25834
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
24191
|
-
import { tokenCache } from "@clerk/
|
|
25835
|
+
import { tokenCache } from "@clerk/expo/token-cache";
|
|
24192
25836
|
{{/if}}
|
|
24193
25837
|
{{else}}
|
|
24194
25838
|
{{#unless (eq api "none")}}
|
|
@@ -24210,6 +25854,22 @@ const convex = new ConvexReactClient(env.EXPO_PUBLIC_CONVEX_URL, {
|
|
|
24210
25854
|
});
|
|
24211
25855
|
{{/if}}
|
|
24212
25856
|
|
|
25857
|
+
{{#if (and (eq auth "clerk") (ne api "none") (ne backend "convex"))}}
|
|
25858
|
+
function ClerkApiAuthBridge() {
|
|
25859
|
+
const { getToken } = useAuth();
|
|
25860
|
+
|
|
25861
|
+
useEffect(() => {
|
|
25862
|
+
setClerkAuthTokenGetter(getToken);
|
|
25863
|
+
|
|
25864
|
+
return () => {
|
|
25865
|
+
setClerkAuthTokenGetter(null);
|
|
25866
|
+
};
|
|
25867
|
+
}, [getToken]);
|
|
25868
|
+
|
|
25869
|
+
return null;
|
|
25870
|
+
}
|
|
25871
|
+
{{/if}}
|
|
25872
|
+
|
|
24213
25873
|
export default function RootLayout() {
|
|
24214
25874
|
const { theme } = useUnistyles();
|
|
24215
25875
|
|
|
@@ -24289,49 +25949,103 @@ export default function RootLayout() {
|
|
|
24289
25949
|
</ConvexProvider>
|
|
24290
25950
|
{{/if}}
|
|
24291
25951
|
{{else}}
|
|
24292
|
-
{{#
|
|
24293
|
-
|
|
24294
|
-
|
|
24295
|
-
|
|
24296
|
-
|
|
24297
|
-
|
|
24298
|
-
|
|
24299
|
-
|
|
24300
|
-
|
|
24301
|
-
|
|
24302
|
-
|
|
24303
|
-
|
|
24304
|
-
|
|
24305
|
-
|
|
24306
|
-
|
|
24307
|
-
|
|
24308
|
-
|
|
24309
|
-
|
|
24310
|
-
|
|
24311
|
-
|
|
24312
|
-
|
|
24313
|
-
|
|
25952
|
+
{{#if (eq auth "clerk")}}
|
|
25953
|
+
<ClerkProvider
|
|
25954
|
+
tokenCache={tokenCache}
|
|
25955
|
+
publishableKey={env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY}
|
|
25956
|
+
>
|
|
25957
|
+
{{#unless (eq api "none")}}
|
|
25958
|
+
<ClerkApiAuthBridge />
|
|
25959
|
+
<QueryClientProvider client={queryClient}>
|
|
25960
|
+
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
25961
|
+
<Stack
|
|
25962
|
+
screenOptions=\\{{
|
|
25963
|
+
headerStyle: {
|
|
25964
|
+
backgroundColor: theme.colors.background,
|
|
25965
|
+
},
|
|
25966
|
+
headerTitleStyle: {
|
|
25967
|
+
color: theme.colors.foreground,
|
|
25968
|
+
},
|
|
25969
|
+
headerTintColor: theme.colors.foreground,
|
|
25970
|
+
}}
|
|
25971
|
+
>
|
|
25972
|
+
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
25973
|
+
<Stack.Screen name="(auth)" options=\\{{ headerShown: false }} />
|
|
25974
|
+
<Stack.Screen
|
|
25975
|
+
name="modal"
|
|
25976
|
+
options=\\{{ title: "Modal", presentation: "modal" }}
|
|
25977
|
+
/>
|
|
25978
|
+
</Stack>
|
|
25979
|
+
</GestureHandlerRootView>
|
|
25980
|
+
</QueryClientProvider>
|
|
25981
|
+
{{else}}
|
|
25982
|
+
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
25983
|
+
<Stack
|
|
25984
|
+
screenOptions=\\{{
|
|
25985
|
+
headerStyle: {
|
|
25986
|
+
backgroundColor: theme.colors.background,
|
|
25987
|
+
},
|
|
25988
|
+
headerTitleStyle: {
|
|
25989
|
+
color: theme.colors.foreground,
|
|
25990
|
+
},
|
|
25991
|
+
headerTintColor: theme.colors.foreground,
|
|
25992
|
+
}}
|
|
25993
|
+
>
|
|
25994
|
+
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
25995
|
+
<Stack.Screen name="(auth)" options=\\{{ headerShown: false }} />
|
|
25996
|
+
<Stack.Screen
|
|
25997
|
+
name="modal"
|
|
25998
|
+
options=\\{{ title: "Modal", presentation: "modal" }}
|
|
25999
|
+
/>
|
|
26000
|
+
</Stack>
|
|
26001
|
+
</GestureHandlerRootView>
|
|
26002
|
+
{{/unless}}
|
|
26003
|
+
</ClerkProvider>
|
|
24314
26004
|
{{else}}
|
|
24315
|
-
|
|
24316
|
-
|
|
24317
|
-
|
|
24318
|
-
|
|
24319
|
-
|
|
24320
|
-
|
|
24321
|
-
|
|
24322
|
-
|
|
24323
|
-
|
|
24324
|
-
|
|
24325
|
-
|
|
24326
|
-
|
|
24327
|
-
|
|
24328
|
-
|
|
24329
|
-
name="
|
|
24330
|
-
|
|
24331
|
-
|
|
24332
|
-
|
|
24333
|
-
|
|
24334
|
-
|
|
26005
|
+
{{#unless (eq api "none")}}
|
|
26006
|
+
<QueryClientProvider client={queryClient}>
|
|
26007
|
+
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
26008
|
+
<Stack
|
|
26009
|
+
screenOptions=\\{{
|
|
26010
|
+
headerStyle: {
|
|
26011
|
+
backgroundColor: theme.colors.background,
|
|
26012
|
+
},
|
|
26013
|
+
headerTitleStyle: {
|
|
26014
|
+
color: theme.colors.foreground,
|
|
26015
|
+
},
|
|
26016
|
+
headerTintColor: theme.colors.foreground,
|
|
26017
|
+
}}
|
|
26018
|
+
>
|
|
26019
|
+
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
26020
|
+
<Stack.Screen
|
|
26021
|
+
name="modal"
|
|
26022
|
+
options=\\{{ title: "Modal", presentation: "modal" }}
|
|
26023
|
+
/>
|
|
26024
|
+
</Stack>
|
|
26025
|
+
</GestureHandlerRootView>
|
|
26026
|
+
</QueryClientProvider>
|
|
26027
|
+
{{else}}
|
|
26028
|
+
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
26029
|
+
<Stack
|
|
26030
|
+
screenOptions=\\{{
|
|
26031
|
+
headerStyle: {
|
|
26032
|
+
backgroundColor: theme.colors.background,
|
|
26033
|
+
},
|
|
26034
|
+
headerTitleStyle: {
|
|
26035
|
+
color: theme.colors.foreground,
|
|
26036
|
+
},
|
|
26037
|
+
headerTintColor: theme.colors.foreground,
|
|
26038
|
+
}}
|
|
26039
|
+
>
|
|
26040
|
+
<Stack.Screen name="(drawer)" options=\\{{ headerShown: false }} />
|
|
26041
|
+
<Stack.Screen
|
|
26042
|
+
name="modal"
|
|
26043
|
+
options=\\{{ title: "Modal", presentation: "modal" }}
|
|
26044
|
+
/>
|
|
26045
|
+
</Stack>
|
|
26046
|
+
</GestureHandlerRootView>
|
|
26047
|
+
{{/unless}}
|
|
26048
|
+
{{/if}}
|
|
24335
26049
|
{{/if}}
|
|
24336
26050
|
);
|
|
24337
26051
|
}
|
|
@@ -24556,7 +26270,11 @@ import { trpc } from "@/utils/trpc";
|
|
|
24556
26270
|
import { Link } from "expo-router";
|
|
24557
26271
|
import { Authenticated, AuthLoading, Unauthenticated, useQuery } from "convex/react";
|
|
24558
26272
|
import { api } from "@{{ projectName }}/backend/convex/_generated/api";
|
|
24559
|
-
import { useUser } from "@clerk/
|
|
26273
|
+
import { useUser } from "@clerk/expo";
|
|
26274
|
+
import { SignOutButton } from "@/components/sign-out-button";
|
|
26275
|
+
{{else if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
26276
|
+
import { Link } from "expo-router";
|
|
26277
|
+
import { useAuth, useUser } from "@clerk/expo";
|
|
24560
26278
|
import { SignOutButton } from "@/components/sign-out-button";
|
|
24561
26279
|
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
24562
26280
|
import { useConvexAuth, useQuery } from "convex/react";
|
|
@@ -24580,6 +26298,9 @@ export default function Home() {
|
|
|
24580
26298
|
const { user } = useUser();
|
|
24581
26299
|
const healthCheck = useQuery(api.healthCheck.get);
|
|
24582
26300
|
const privateData = useQuery(api.privateData.get);
|
|
26301
|
+
{{else if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
26302
|
+
const { isLoaded, isSignedIn } = useAuth();
|
|
26303
|
+
const { user } = useUser();
|
|
24583
26304
|
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
24584
26305
|
const healthCheck = useQuery(api.healthCheck.get);
|
|
24585
26306
|
const { isAuthenticated } = useConvexAuth();
|
|
@@ -24682,6 +26403,32 @@ export default function Home() {
|
|
|
24682
26403
|
</AuthLoading>
|
|
24683
26404
|
{{/if}}
|
|
24684
26405
|
|
|
26406
|
+
{{#if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
26407
|
+
{!isLoaded ? (
|
|
26408
|
+
<Text style={styles.apiStatusText}>Loading...</Text>
|
|
26409
|
+
) : isSignedIn ? (
|
|
26410
|
+
<View style={styles.userCard}>
|
|
26411
|
+
<View style={styles.userHeader}>
|
|
26412
|
+
<Text style={styles.userWelcome}>
|
|
26413
|
+
Welcome,{" "}
|
|
26414
|
+
<Text style={styles.userName}>{user?.fullName ?? user?.firstName ?? "there"}</Text>
|
|
26415
|
+
</Text>
|
|
26416
|
+
</View>
|
|
26417
|
+
<Text style={styles.userEmail}>{user?.emailAddresses[0]?.emailAddress}</Text>
|
|
26418
|
+
<SignOutButton />
|
|
26419
|
+
</View>
|
|
26420
|
+
) : (
|
|
26421
|
+
<>
|
|
26422
|
+
<Link href="/(auth)/sign-in">
|
|
26423
|
+
<Text style={styles.apiStatusText}>Sign in</Text>
|
|
26424
|
+
</Link>
|
|
26425
|
+
<Link href="/(auth)/sign-up">
|
|
26426
|
+
<Text style={styles.apiStatusText}>Sign up</Text>
|
|
26427
|
+
</Link>
|
|
26428
|
+
</>
|
|
26429
|
+
)}
|
|
26430
|
+
{{/if}}
|
|
26431
|
+
|
|
24685
26432
|
{{#if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
24686
26433
|
{user ? (
|
|
24687
26434
|
<View style={styles.userCard}>
|
|
@@ -24872,7 +26619,8 @@ const styles = StyleSheet.create((theme) => ({
|
|
|
24872
26619
|
apiStatusText: {
|
|
24873
26620
|
color: theme.colors.mutedForeground,
|
|
24874
26621
|
},
|
|
24875
|
-
}))
|
|
26622
|
+
}));
|
|
26623
|
+
`],
|
|
24876
26624
|
["frontend/native/unistyles/app/+html.tsx.hbs", `import { ScrollViewStyleReset } from "expo-router/html";
|
|
24877
26625
|
import { type PropsWithChildren } from "react";
|
|
24878
26626
|
|
|
@@ -25349,8 +27097,17 @@ uniwind-types.d.ts
|
|
|
25349
27097
|
["frontend/native/uniwind/app/_layout.tsx.hbs", `{{#if (includes examples "ai")}}
|
|
25350
27098
|
import "@/polyfills";
|
|
25351
27099
|
{{/if}}
|
|
27100
|
+
{{#if (and (eq auth "clerk") (ne api "none") (ne backend "convex"))}}
|
|
27101
|
+
import { useEffect } from "react";
|
|
27102
|
+
import { setClerkAuthTokenGetter } from "@/utils/clerk-auth";
|
|
27103
|
+
{{/if}}
|
|
25352
27104
|
|
|
25353
27105
|
import "@/global.css";
|
|
27106
|
+
{{#if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
27107
|
+
import { ClerkProvider{{#unless (eq api "none")}}, useAuth{{/unless}} } from "@clerk/expo";
|
|
27108
|
+
import { tokenCache } from "@clerk/expo/token-cache";
|
|
27109
|
+
import { env } from "@{{projectName}}/env/native";
|
|
27110
|
+
{{/if}}
|
|
25354
27111
|
|
|
25355
27112
|
{{#if (eq backend "convex")}}
|
|
25356
27113
|
{{#if (eq auth "better-auth")}}
|
|
@@ -25364,9 +27121,9 @@ import "@/global.css";
|
|
|
25364
27121
|
{{/if}}
|
|
25365
27122
|
|
|
25366
27123
|
{{#if (eq auth "clerk")}}
|
|
25367
|
-
import { ClerkProvider, useAuth } from "@clerk/
|
|
27124
|
+
import { ClerkProvider, useAuth } from "@clerk/expo";
|
|
25368
27125
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
25369
|
-
import { tokenCache } from "@clerk/
|
|
27126
|
+
import { tokenCache } from "@clerk/expo/token-cache";
|
|
25370
27127
|
{{/if}}
|
|
25371
27128
|
{{else}}
|
|
25372
27129
|
{{#unless (eq api "none")}}
|
|
@@ -25397,6 +27154,22 @@ export const unstable_settings = {
|
|
|
25397
27154
|
});
|
|
25398
27155
|
{{/if}}
|
|
25399
27156
|
|
|
27157
|
+
{{#if (and (eq auth "clerk") (ne api "none") (ne backend "convex"))}}
|
|
27158
|
+
function ClerkApiAuthBridge() {
|
|
27159
|
+
const { getToken } = useAuth();
|
|
27160
|
+
|
|
27161
|
+
useEffect(() => {
|
|
27162
|
+
setClerkAuthTokenGetter(getToken);
|
|
27163
|
+
|
|
27164
|
+
return () => {
|
|
27165
|
+
setClerkAuthTokenGetter(null);
|
|
27166
|
+
};
|
|
27167
|
+
}, [getToken]);
|
|
27168
|
+
|
|
27169
|
+
return null;
|
|
27170
|
+
}
|
|
27171
|
+
{{/if}}
|
|
27172
|
+
|
|
25400
27173
|
function StackLayout() {
|
|
25401
27174
|
return (
|
|
25402
27175
|
<Stack screenOptions=\\{{}}>
|
|
@@ -25449,35 +27222,65 @@ export default function Layout() {
|
|
|
25449
27222
|
</AppThemeProvider>
|
|
25450
27223
|
</KeyboardProvider>
|
|
25451
27224
|
</GestureHandlerRootView>
|
|
25452
|
-
</ConvexProvider>
|
|
27225
|
+
</ConvexProvider>
|
|
27226
|
+
{{/if}}
|
|
27227
|
+
{{else}}
|
|
27228
|
+
{{#if (eq auth "clerk")}}
|
|
27229
|
+
<ClerkProvider tokenCache={tokenCache} publishableKey={env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY}>
|
|
27230
|
+
{{#unless (eq api "none")}}
|
|
27231
|
+
<ClerkApiAuthBridge />
|
|
27232
|
+
<QueryClientProvider client={queryClient}>
|
|
27233
|
+
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
27234
|
+
<KeyboardProvider>
|
|
27235
|
+
<AppThemeProvider>
|
|
27236
|
+
<HeroUINativeProvider>
|
|
27237
|
+
<StackLayout />
|
|
27238
|
+
</HeroUINativeProvider>
|
|
27239
|
+
</AppThemeProvider>
|
|
27240
|
+
</KeyboardProvider>
|
|
27241
|
+
</GestureHandlerRootView>
|
|
27242
|
+
</QueryClientProvider>
|
|
27243
|
+
{{else}}
|
|
27244
|
+
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
27245
|
+
<KeyboardProvider>
|
|
27246
|
+
<AppThemeProvider>
|
|
27247
|
+
<HeroUINativeProvider>
|
|
27248
|
+
<StackLayout />
|
|
27249
|
+
</HeroUINativeProvider>
|
|
27250
|
+
</AppThemeProvider>
|
|
27251
|
+
</KeyboardProvider>
|
|
27252
|
+
</GestureHandlerRootView>
|
|
27253
|
+
{{/unless}}
|
|
27254
|
+
</ClerkProvider>
|
|
27255
|
+
{{else}}
|
|
27256
|
+
{{#unless (eq api "none")}}
|
|
27257
|
+
<QueryClientProvider client={queryClient}>
|
|
27258
|
+
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
27259
|
+
<KeyboardProvider>
|
|
27260
|
+
<AppThemeProvider>
|
|
27261
|
+
<HeroUINativeProvider>
|
|
27262
|
+
<StackLayout />
|
|
27263
|
+
</HeroUINativeProvider>
|
|
27264
|
+
</AppThemeProvider>
|
|
27265
|
+
</KeyboardProvider>
|
|
27266
|
+
</GestureHandlerRootView>
|
|
27267
|
+
</QueryClientProvider>
|
|
27268
|
+
{{else}}
|
|
27269
|
+
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
27270
|
+
<KeyboardProvider>
|
|
27271
|
+
<AppThemeProvider>
|
|
27272
|
+
<HeroUINativeProvider>
|
|
27273
|
+
<StackLayout />
|
|
27274
|
+
</HeroUINativeProvider>
|
|
27275
|
+
</AppThemeProvider>
|
|
27276
|
+
</KeyboardProvider>
|
|
27277
|
+
</GestureHandlerRootView>
|
|
27278
|
+
{{/unless}}
|
|
25453
27279
|
{{/if}}
|
|
25454
|
-
{{else}}
|
|
25455
|
-
{{#unless (eq api "none")}}
|
|
25456
|
-
<QueryClientProvider client={queryClient}>
|
|
25457
|
-
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
25458
|
-
<KeyboardProvider>
|
|
25459
|
-
<AppThemeProvider>
|
|
25460
|
-
<HeroUINativeProvider>
|
|
25461
|
-
<StackLayout />
|
|
25462
|
-
</HeroUINativeProvider>
|
|
25463
|
-
</AppThemeProvider>
|
|
25464
|
-
</KeyboardProvider>
|
|
25465
|
-
</GestureHandlerRootView>
|
|
25466
|
-
</QueryClientProvider>
|
|
25467
|
-
{{else}}
|
|
25468
|
-
<GestureHandlerRootView style=\\{{ flex: 1 }}>
|
|
25469
|
-
<KeyboardProvider>
|
|
25470
|
-
<AppThemeProvider>
|
|
25471
|
-
<HeroUINativeProvider>
|
|
25472
|
-
<StackLayout />
|
|
25473
|
-
</HeroUINativeProvider>
|
|
25474
|
-
</AppThemeProvider>
|
|
25475
|
-
</KeyboardProvider>
|
|
25476
|
-
</GestureHandlerRootView>
|
|
25477
|
-
{{/unless}}
|
|
25478
27280
|
{{/if}}
|
|
25479
27281
|
);
|
|
25480
|
-
}
|
|
27282
|
+
}
|
|
27283
|
+
`],
|
|
25481
27284
|
["frontend/native/uniwind/app/(drawer)/_layout.tsx.hbs", `import React, { useCallback } from "react";
|
|
25482
27285
|
import { Ionicons, MaterialIcons } from "@expo/vector-icons";
|
|
25483
27286
|
import { Link } from "expo-router";
|
|
@@ -25662,7 +27465,11 @@ import { trpc } from "@/utils/trpc";
|
|
|
25662
27465
|
import { Link } from "expo-router";
|
|
25663
27466
|
import { Authenticated, AuthLoading, Unauthenticated, useQuery } from "convex/react";
|
|
25664
27467
|
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
25665
|
-
import { useUser } from "@clerk/
|
|
27468
|
+
import { useUser } from "@clerk/expo";
|
|
27469
|
+
import { SignOutButton } from "@/components/sign-out-button";
|
|
27470
|
+
{{else if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
27471
|
+
import { Link } from "expo-router";
|
|
27472
|
+
import { useAuth, useUser } from "@clerk/expo";
|
|
25666
27473
|
import { SignOutButton } from "@/components/sign-out-button";
|
|
25667
27474
|
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
25668
27475
|
import { useConvexAuth, useQuery } from "convex/react";
|
|
@@ -25690,6 +27497,9 @@ const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
|
25690
27497
|
const { user } = useUser();
|
|
25691
27498
|
const healthCheck = useQuery(api.healthCheck.get);
|
|
25692
27499
|
const privateData = useQuery(api.privateData.get);
|
|
27500
|
+
{{else if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
27501
|
+
const { isLoaded, isSignedIn } = useAuth();
|
|
27502
|
+
const { user } = useUser();
|
|
25693
27503
|
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
25694
27504
|
const healthCheck = useQuery(api.healthCheck.get);
|
|
25695
27505
|
const { isAuthenticated } = useConvexAuth();
|
|
@@ -25796,6 +27606,37 @@ return (
|
|
|
25796
27606
|
</AuthLoading>
|
|
25797
27607
|
{{/if}}
|
|
25798
27608
|
|
|
27609
|
+
{{#if (and (ne backend "convex") (eq auth "clerk"))}}
|
|
27610
|
+
{!isLoaded ? (
|
|
27611
|
+
<View className="mt-4 items-center">
|
|
27612
|
+
<Spinner size="sm" />
|
|
27613
|
+
</View>
|
|
27614
|
+
) : isSignedIn ? (
|
|
27615
|
+
<Surface variant="secondary" className="mt-5 p-4 rounded-xl">
|
|
27616
|
+
<View className="flex-row items-center justify-between">
|
|
27617
|
+
<View className="flex-1">
|
|
27618
|
+
<Text className="text-foreground font-medium">
|
|
27619
|
+
{user?.fullName ?? user?.firstName ?? "Welcome"}
|
|
27620
|
+
</Text>
|
|
27621
|
+
<Text className="text-muted text-xs mt-0.5">
|
|
27622
|
+
{user?.emailAddresses[0]?.emailAddress}
|
|
27623
|
+
</Text>
|
|
27624
|
+
</View>
|
|
27625
|
+
<SignOutButton />
|
|
27626
|
+
</View>
|
|
27627
|
+
</Surface>
|
|
27628
|
+
) : (
|
|
27629
|
+
<View className="mt-4 gap-3">
|
|
27630
|
+
<Link href="/(auth)/sign-in" asChild>
|
|
27631
|
+
<Button variant="secondary"><Button.Label>Sign In</Button.Label></Button>
|
|
27632
|
+
</Link>
|
|
27633
|
+
<Link href="/(auth)/sign-up" asChild>
|
|
27634
|
+
<Button variant="ghost"><Button.Label>Sign Up</Button.Label></Button>
|
|
27635
|
+
</Link>
|
|
27636
|
+
</View>
|
|
27637
|
+
)}
|
|
27638
|
+
{{/if}}
|
|
27639
|
+
|
|
25799
27640
|
{{#if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
25800
27641
|
{user ? (
|
|
25801
27642
|
<Surface variant="secondary" className="mb-4 p-4 rounded-xl">
|
|
@@ -26142,6 +27983,8 @@ module.exports = uniwindConfig;
|
|
|
26142
27983
|
"**/*.tsx"
|
|
26143
27984
|
]
|
|
26144
27985
|
}`],
|
|
27986
|
+
["frontend/native/uniwind/uniwind-env.d.ts", `/// <reference types="uniwind/types" />
|
|
27987
|
+
`],
|
|
26145
27988
|
["frontend/nuxt/_gitignore", `# Nuxt dev/build outputs
|
|
26146
27989
|
.output
|
|
26147
27990
|
.data
|
|
@@ -26479,7 +28322,7 @@ initOpenNextCloudflareForDev();
|
|
|
26479
28322
|
"dependencies": {
|
|
26480
28323
|
"@{{projectName}}/ui": "{{#if (eq packageManager "npm")}}*{{else}}workspace:*{{/if}}",
|
|
26481
28324
|
"lucide-react": "^0.546.0",
|
|
26482
|
-
"next": "^16.
|
|
28325
|
+
"next": "^16.2.0",
|
|
26483
28326
|
"next-themes": "^0.4.6",
|
|
26484
28327
|
"react": "^19.2.3",
|
|
26485
28328
|
"react-dom": "^19.2.3",
|
|
@@ -26506,8 +28349,8 @@ export default config;
|
|
|
26506
28349
|
["frontend/react/next/src/app/layout.tsx.hbs", `import type { Metadata } from "next";
|
|
26507
28350
|
import { Geist, Geist_Mono } from "next/font/google";
|
|
26508
28351
|
import "../index.css";
|
|
26509
|
-
{{#if (eq auth "clerk")}}
|
|
26510
|
-
{{/if}}{{
|
|
28352
|
+
{{#if (eq auth "clerk")}}import { ClerkProvider } from "@clerk/nextjs";
|
|
28353
|
+
{{/if}}{{#if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
26511
28354
|
import { getToken } from "@/lib/auth-server";
|
|
26512
28355
|
{{/if}}
|
|
26513
28356
|
import Providers from "@/components/providers";
|
|
@@ -26556,12 +28399,12 @@ export default function RootLayout({
|
|
|
26556
28399
|
}: Readonly<{
|
|
26557
28400
|
children: React.ReactNode;
|
|
26558
28401
|
}>) {
|
|
26559
|
-
|
|
28402
|
+
return (
|
|
26560
28403
|
<html lang="en" suppressHydrationWarning>
|
|
26561
28404
|
<body
|
|
26562
28405
|
className={\`\${geistSans.variable} \${geistMono.variable} antialiased\`}
|
|
26563
28406
|
>
|
|
26564
|
-
{{#if (
|
|
28407
|
+
{{#if (eq auth "clerk")}}<ClerkProvider>
|
|
26565
28408
|
<Providers>
|
|
26566
28409
|
<div className="grid grid-rows-[auto_1fr] h-svh">
|
|
26567
28410
|
<Header />
|
|
@@ -26700,9 +28543,15 @@ export function ModeToggle() {
|
|
|
26700
28543
|
`],
|
|
26701
28544
|
["frontend/react/next/src/components/providers.tsx.hbs", `"use client";
|
|
26702
28545
|
|
|
28546
|
+
{{#if (and (eq auth "clerk") (ne api "none"))}}
|
|
28547
|
+
import { useEffect } from "react";
|
|
28548
|
+
import { setClerkAuthTokenGetter } from "@/utils/clerk-auth";
|
|
28549
|
+
{{/if}}
|
|
28550
|
+
{{#if (and (eq auth "clerk") (or (eq backend "convex") (ne api "none")))}}
|
|
28551
|
+
import { useAuth } from "@clerk/nextjs";
|
|
28552
|
+
{{/if}}
|
|
26703
28553
|
{{#if (eq backend "convex")}}
|
|
26704
28554
|
{{#if (eq auth "clerk")}}
|
|
26705
|
-
import { useAuth } from "@clerk/nextjs";
|
|
26706
28555
|
import { ConvexReactClient } from "convex/react";
|
|
26707
28556
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
26708
28557
|
import { env } from "@{{projectName}}/env/web";
|
|
@@ -26734,6 +28583,22 @@ import { Toaster } from "@{{projectName}}/ui/components/sonner";
|
|
|
26734
28583
|
const convex = new ConvexReactClient(env.NEXT_PUBLIC_CONVEX_URL);
|
|
26735
28584
|
{{/if}}
|
|
26736
28585
|
|
|
28586
|
+
{{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
|
|
28587
|
+
function ClerkApiAuthBridge() {
|
|
28588
|
+
const { getToken } = useAuth();
|
|
28589
|
+
|
|
28590
|
+
useEffect(() => {
|
|
28591
|
+
setClerkAuthTokenGetter(getToken);
|
|
28592
|
+
|
|
28593
|
+
return () => {
|
|
28594
|
+
setClerkAuthTokenGetter(null);
|
|
28595
|
+
};
|
|
28596
|
+
}, [getToken]);
|
|
28597
|
+
|
|
28598
|
+
return null;
|
|
28599
|
+
}
|
|
28600
|
+
{{/if}}
|
|
28601
|
+
|
|
26737
28602
|
export default function Providers({
|
|
26738
28603
|
children,
|
|
26739
28604
|
{{#if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
@@ -26771,6 +28636,9 @@ export default function Providers({
|
|
|
26771
28636
|
{{else}}
|
|
26772
28637
|
{{#unless (eq api "none")}}
|
|
26773
28638
|
<QueryClientProvider client={queryClient}>
|
|
28639
|
+
{{#if (eq auth "clerk")}}
|
|
28640
|
+
<ClerkApiAuthBridge />
|
|
28641
|
+
{{/if}}
|
|
26774
28642
|
{{#if (eq api "orpc")}}
|
|
26775
28643
|
{children}
|
|
26776
28644
|
{{/if}}
|
|
@@ -26815,7 +28683,7 @@ export function ThemeProvider({
|
|
|
26815
28683
|
"resolveJsonModule": true,
|
|
26816
28684
|
"isolatedModules": true,
|
|
26817
28685
|
"verbatimModuleSyntax": true,
|
|
26818
|
-
"jsx": "
|
|
28686
|
+
"jsx": "react-jsx",
|
|
26819
28687
|
"incremental": true,
|
|
26820
28688
|
"plugins": [
|
|
26821
28689
|
{
|
|
@@ -26834,10 +28702,11 @@ export function ThemeProvider({
|
|
|
26834
28702
|
{{#if (eq serverDeploy "cloudflare")}}
|
|
26835
28703
|
"../server/env.d.ts",
|
|
26836
28704
|
{{/if}}
|
|
26837
|
-
"
|
|
26838
|
-
"
|
|
26839
|
-
"
|
|
26840
|
-
"
|
|
28705
|
+
"next-env.d.ts",
|
|
28706
|
+
"**/*.ts",
|
|
28707
|
+
"**/*.tsx",
|
|
28708
|
+
".next/types/**/*.ts",
|
|
28709
|
+
".next/dev/types/**/*.ts"
|
|
26841
28710
|
],
|
|
26842
28711
|
"exclude": [
|
|
26843
28712
|
"./node_modules"
|
|
@@ -26887,6 +28756,9 @@ export function ThemeProvider({
|
|
|
26887
28756
|
export default {
|
|
26888
28757
|
ssr: false,
|
|
26889
28758
|
appDirectory: "src",
|
|
28759
|
+
future: {
|
|
28760
|
+
v8_middleware: true,
|
|
28761
|
+
},
|
|
26890
28762
|
} satisfies Config;
|
|
26891
28763
|
`],
|
|
26892
28764
|
["frontend/react/react-router/src/components/mode-toggle.tsx.hbs", `import { Moon, Sun } from "lucide-react";
|
|
@@ -26944,16 +28816,23 @@ import "./index.css";
|
|
|
26944
28816
|
import Header from "./components/header";
|
|
26945
28817
|
import { ThemeProvider } from "./components/theme-provider";
|
|
26946
28818
|
import { Toaster } from "@{{projectName}}/ui/components/sonner";
|
|
28819
|
+
{{#if (eq auth "clerk")}}
|
|
28820
|
+
import { ClerkProvider{{#if (or (eq backend "convex") (ne api "none"))}}, useAuth{{/if}} } from "@clerk/react-router";
|
|
28821
|
+
import { clerkMiddleware, rootAuthLoader } from "@clerk/react-router/server";
|
|
28822
|
+
{{/if}}
|
|
28823
|
+
{{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
|
|
28824
|
+
import { useEffect } from "react";
|
|
28825
|
+
import { setClerkAuthTokenGetter } from "@/utils/clerk-auth";
|
|
28826
|
+
{{/if}}
|
|
26947
28827
|
|
|
26948
28828
|
{{#if (eq backend "convex")}}
|
|
26949
28829
|
import { ConvexReactClient } from "convex/react";
|
|
26950
28830
|
import { env } from "@{{projectName}}/env/web";
|
|
26951
|
-
{{#if (eq auth "clerk")}}
|
|
26952
|
-
import { ClerkProvider, useAuth } from "@clerk/clerk-react";
|
|
28831
|
+
{{#if (eq auth "clerk")}}
|
|
26953
28832
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
26954
|
-
{{else}}
|
|
28833
|
+
{{else}}
|
|
26955
28834
|
import { ConvexProvider } from "convex/react";
|
|
26956
|
-
{{/if}}
|
|
28835
|
+
{{/if}}
|
|
26957
28836
|
{{else}}
|
|
26958
28837
|
{{#unless (eq api "none")}}
|
|
26959
28838
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
@@ -26967,6 +28846,28 @@ import { queryClient } from "./utils/trpc";
|
|
|
26967
28846
|
{{/unless}}
|
|
26968
28847
|
{{/if}}
|
|
26969
28848
|
|
|
28849
|
+
{{#if (eq auth "clerk")}}
|
|
28850
|
+
export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()];
|
|
28851
|
+
|
|
28852
|
+
export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args);
|
|
28853
|
+
{{/if}}
|
|
28854
|
+
|
|
28855
|
+
{{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
|
|
28856
|
+
function ClerkApiAuthBridge() {
|
|
28857
|
+
const { getToken } = useAuth();
|
|
28858
|
+
|
|
28859
|
+
useEffect(() => {
|
|
28860
|
+
setClerkAuthTokenGetter(getToken);
|
|
28861
|
+
|
|
28862
|
+
return () => {
|
|
28863
|
+
setClerkAuthTokenGetter(null);
|
|
28864
|
+
};
|
|
28865
|
+
}, [getToken]);
|
|
28866
|
+
|
|
28867
|
+
return null;
|
|
28868
|
+
}
|
|
28869
|
+
{{/if}}
|
|
28870
|
+
|
|
26970
28871
|
export const links: Route.LinksFunction = () => [
|
|
26971
28872
|
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
|
|
26972
28873
|
{ rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" },
|
|
@@ -26996,11 +28897,15 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
26996
28897
|
}
|
|
26997
28898
|
|
|
26998
28899
|
{{#if (eq backend "convex")}}
|
|
28900
|
+
{{#if (eq auth "clerk")}}
|
|
28901
|
+
export default function App({ loaderData }: Route.ComponentProps) {
|
|
28902
|
+
{{else}}
|
|
26999
28903
|
export default function App() {
|
|
28904
|
+
{{/if}}
|
|
27000
28905
|
const convex = new ConvexReactClient(env.VITE_CONVEX_URL);
|
|
27001
28906
|
{{#if (eq auth "clerk")}}
|
|
27002
28907
|
return (
|
|
27003
|
-
<ClerkProvider
|
|
28908
|
+
<ClerkProvider loaderData={loaderData}>
|
|
27004
28909
|
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
|
|
27005
28910
|
<ThemeProvider
|
|
27006
28911
|
attribute="class"
|
|
@@ -27036,6 +28941,62 @@ export default function App() {
|
|
|
27036
28941
|
);
|
|
27037
28942
|
{{/if}}
|
|
27038
28943
|
}
|
|
28944
|
+
{{else if (eq auth "clerk")}}
|
|
28945
|
+
export default function App({ loaderData }: Route.ComponentProps) {
|
|
28946
|
+
return (
|
|
28947
|
+
<ClerkProvider loaderData={loaderData}>
|
|
28948
|
+
{{#unless (eq api "none")}}
|
|
28949
|
+
<ClerkApiAuthBridge />
|
|
28950
|
+
{{/unless}}
|
|
28951
|
+
{{#if (eq api "orpc")}}
|
|
28952
|
+
<QueryClientProvider client={queryClient}>
|
|
28953
|
+
<ThemeProvider
|
|
28954
|
+
attribute="class"
|
|
28955
|
+
defaultTheme="dark"
|
|
28956
|
+
disableTransitionOnChange
|
|
28957
|
+
storageKey="vite-ui-theme"
|
|
28958
|
+
>
|
|
28959
|
+
<div className="grid grid-rows-[auto_1fr] h-svh">
|
|
28960
|
+
<Header />
|
|
28961
|
+
<Outlet />
|
|
28962
|
+
</div>
|
|
28963
|
+
<Toaster richColors />
|
|
28964
|
+
</ThemeProvider>
|
|
28965
|
+
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
|
|
28966
|
+
</QueryClientProvider>
|
|
28967
|
+
{{else if (eq api "trpc")}}
|
|
28968
|
+
<QueryClientProvider client={queryClient}>
|
|
28969
|
+
<ThemeProvider
|
|
28970
|
+
attribute="class"
|
|
28971
|
+
defaultTheme="dark"
|
|
28972
|
+
disableTransitionOnChange
|
|
28973
|
+
storageKey="vite-ui-theme"
|
|
28974
|
+
>
|
|
28975
|
+
<div className="grid grid-rows-[auto_1fr] h-svh">
|
|
28976
|
+
<Header />
|
|
28977
|
+
<Outlet />
|
|
28978
|
+
</div>
|
|
28979
|
+
<Toaster richColors />
|
|
28980
|
+
</ThemeProvider>
|
|
28981
|
+
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
|
|
28982
|
+
</QueryClientProvider>
|
|
28983
|
+
{{else}}
|
|
28984
|
+
<ThemeProvider
|
|
28985
|
+
attribute="class"
|
|
28986
|
+
defaultTheme="dark"
|
|
28987
|
+
disableTransitionOnChange
|
|
28988
|
+
storageKey="vite-ui-theme"
|
|
28989
|
+
>
|
|
28990
|
+
<div className="grid grid-rows-[auto_1fr] h-svh">
|
|
28991
|
+
<Header />
|
|
28992
|
+
<Outlet />
|
|
28993
|
+
</div>
|
|
28994
|
+
<Toaster richColors />
|
|
28995
|
+
</ThemeProvider>
|
|
28996
|
+
{{/if}}
|
|
28997
|
+
</ClerkProvider>
|
|
28998
|
+
);
|
|
28999
|
+
}
|
|
27039
29000
|
{{else if (eq api "orpc")}}
|
|
27040
29001
|
export default function App() {
|
|
27041
29002
|
return (
|
|
@@ -27278,7 +29239,7 @@ export default defineConfig({
|
|
|
27278
29239
|
"build": "vite build",
|
|
27279
29240
|
"serve": "vite preview",
|
|
27280
29241
|
"start": "vite",
|
|
27281
|
-
"check-types": "tsc --noEmit"
|
|
29242
|
+
"check-types": "vite build && tsc --noEmit"
|
|
27282
29243
|
},
|
|
27283
29244
|
"dependencies": {
|
|
27284
29245
|
"@hookform/resolvers": "^5.1.1",
|
|
@@ -27348,6 +29309,10 @@ export { useTheme } from "next-themes";
|
|
|
27348
29309
|
`],
|
|
27349
29310
|
["frontend/react/tanstack-router/src/main.tsx.hbs", `import { RouterProvider, createRouter } from "@tanstack/react-router";
|
|
27350
29311
|
import ReactDOM from "react-dom/client";
|
|
29312
|
+
{{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
|
|
29313
|
+
import { useEffect } from "react";
|
|
29314
|
+
import { setClerkAuthTokenGetter } from "@/utils/clerk-auth";
|
|
29315
|
+
{{/if}}
|
|
27351
29316
|
import Loader from "./components/loader";
|
|
27352
29317
|
import { routeTree } from "./routeTree.gen";
|
|
27353
29318
|
|
|
@@ -27359,11 +29324,15 @@ import { routeTree } from "./routeTree.gen";
|
|
|
27359
29324
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
27360
29325
|
import { queryClient, trpc } from "./utils/trpc";
|
|
27361
29326
|
{{/if}}
|
|
29327
|
+
{{#if (or (eq backend "convex") (eq auth "clerk"))}}
|
|
29328
|
+
import { env } from "@{{projectName}}/env/web";
|
|
29329
|
+
{{/if}}
|
|
29330
|
+
{{#if (eq auth "clerk")}}
|
|
29331
|
+
import { ClerkProvider{{#if (or (eq backend "convex") (ne api "none"))}}, useAuth{{/if}} } from "@clerk/react";
|
|
29332
|
+
{{/if}}
|
|
27362
29333
|
{{#if (eq backend "convex")}}
|
|
27363
29334
|
import { ConvexReactClient } from "convex/react";
|
|
27364
|
-
import { env } from "@{{projectName}}/env/web";
|
|
27365
29335
|
{{#if (eq auth "clerk")}}
|
|
27366
|
-
import { ClerkProvider, useAuth } from "@clerk/clerk-react";
|
|
27367
29336
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
27368
29337
|
{{else if (eq auth "better-auth")}}
|
|
27369
29338
|
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
|
|
@@ -27374,6 +29343,22 @@ import { routeTree } from "./routeTree.gen";
|
|
|
27374
29343
|
const convex = new ConvexReactClient(env.VITE_CONVEX_URL);
|
|
27375
29344
|
{{/if}}
|
|
27376
29345
|
|
|
29346
|
+
{{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
|
|
29347
|
+
function ClerkApiAuthBridge() {
|
|
29348
|
+
const { getToken } = useAuth();
|
|
29349
|
+
|
|
29350
|
+
useEffect(() => {
|
|
29351
|
+
setClerkAuthTokenGetter(getToken);
|
|
29352
|
+
|
|
29353
|
+
return () => {
|
|
29354
|
+
setClerkAuthTokenGetter(null);
|
|
29355
|
+
};
|
|
29356
|
+
}, [getToken]);
|
|
29357
|
+
|
|
29358
|
+
return null;
|
|
29359
|
+
}
|
|
29360
|
+
{{/if}}
|
|
29361
|
+
|
|
27377
29362
|
const router = createRouter({
|
|
27378
29363
|
routeTree,
|
|
27379
29364
|
defaultPreload: "intent",
|
|
@@ -27382,18 +29367,36 @@ const router = createRouter({
|
|
|
27382
29367
|
context: { orpc, queryClient },
|
|
27383
29368
|
Wrap: function WrapComponent({ children }: { children: React.ReactNode }) {
|
|
27384
29369
|
return (
|
|
29370
|
+
{{#if (eq auth "clerk")}}
|
|
29371
|
+
<ClerkProvider publishableKey={env.VITE_CLERK_PUBLISHABLE_KEY}>
|
|
29372
|
+
<ClerkApiAuthBridge />
|
|
29373
|
+
<QueryClientProvider client={queryClient}>
|
|
29374
|
+
{children}
|
|
29375
|
+
</QueryClientProvider>
|
|
29376
|
+
</ClerkProvider>
|
|
29377
|
+
{{else}}
|
|
27385
29378
|
<QueryClientProvider client={queryClient}>
|
|
27386
29379
|
{children}
|
|
27387
29380
|
</QueryClientProvider>
|
|
29381
|
+
{{/if}}
|
|
27388
29382
|
);
|
|
27389
29383
|
},
|
|
27390
29384
|
{{else if (eq api "trpc")}}
|
|
27391
29385
|
context: { trpc, queryClient },
|
|
27392
29386
|
Wrap: function WrapComponent({ children }: { children: React.ReactNode }) {
|
|
27393
29387
|
return (
|
|
29388
|
+
{{#if (eq auth "clerk")}}
|
|
29389
|
+
<ClerkProvider publishableKey={env.VITE_CLERK_PUBLISHABLE_KEY}>
|
|
29390
|
+
<ClerkApiAuthBridge />
|
|
29391
|
+
<QueryClientProvider client={queryClient}>
|
|
29392
|
+
{children}
|
|
29393
|
+
</QueryClientProvider>
|
|
29394
|
+
</ClerkProvider>
|
|
29395
|
+
{{else}}
|
|
27394
29396
|
<QueryClientProvider client={queryClient}>
|
|
27395
29397
|
{children}
|
|
27396
29398
|
</QueryClientProvider>
|
|
29399
|
+
{{/if}}
|
|
27397
29400
|
);
|
|
27398
29401
|
},
|
|
27399
29402
|
{{else if (eq backend "convex")}}
|
|
@@ -27415,6 +29418,11 @@ const router = createRouter({
|
|
|
27415
29418
|
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
|
|
27416
29419
|
{{/if}}
|
|
27417
29420
|
},
|
|
29421
|
+
{{else if (eq auth "clerk")}}
|
|
29422
|
+
context: {},
|
|
29423
|
+
Wrap: function WrapComponent({ children }: { children: React.ReactNode }) {
|
|
29424
|
+
return <ClerkProvider publishableKey={env.VITE_CLERK_PUBLISHABLE_KEY}>{children}</ClerkProvider>;
|
|
29425
|
+
},
|
|
27418
29426
|
{{else}}
|
|
27419
29427
|
context: {},
|
|
27420
29428
|
{{/if}}
|
|
@@ -27734,6 +29742,9 @@ import { TRPCProvider } from "./utils/trpc";
|
|
|
27734
29742
|
{{#unless (eq backend "self")}}
|
|
27735
29743
|
import { env } from "@{{projectName}}/env/web";
|
|
27736
29744
|
{{/unless}}
|
|
29745
|
+
{{#if (eq auth "clerk")}}
|
|
29746
|
+
import { getClerkAuthToken } from "@/utils/clerk-auth";
|
|
29747
|
+
{{/if}}
|
|
27737
29748
|
{{else if (eq api "orpc")}}
|
|
27738
29749
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
27739
29750
|
import { orpc, queryClient } from "./utils/orpc";
|
|
@@ -27794,6 +29805,12 @@ const trpcClient = createTRPCClient<AppRouter>({
|
|
|
27794
29805
|
links: [
|
|
27795
29806
|
httpBatchLink({
|
|
27796
29807
|
url: {{#if (eq backend "self")}}"/api/trpc"{{else}}\`\${env.VITE_SERVER_URL}/trpc\`{{/if}},
|
|
29808
|
+
{{#if (eq auth "clerk")}}
|
|
29809
|
+
headers: async () => {
|
|
29810
|
+
const token = await getClerkAuthToken();
|
|
29811
|
+
return token ? { Authorization: \`Bearer \${token}\` } : {};
|
|
29812
|
+
},
|
|
29813
|
+
{{/if}}
|
|
27797
29814
|
{{#if (eq auth "better-auth")}}
|
|
27798
29815
|
fetch(url, options) {
|
|
27799
29816
|
return fetch(url, {
|
|
@@ -27880,8 +29897,14 @@ import type { QueryClient } from "@tanstack/react-query";
|
|
|
27880
29897
|
{{/if}}
|
|
27881
29898
|
{{/if}}
|
|
27882
29899
|
|
|
29900
|
+
{{#if (eq auth "clerk")}}
|
|
29901
|
+
import { ClerkProvider{{#if (or (eq backend "convex") (ne api "none"))}}, useAuth{{/if}} } from "@clerk/tanstack-react-start";
|
|
29902
|
+
{{/if}}
|
|
29903
|
+
{{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
|
|
29904
|
+
import { useEffect } from "react";
|
|
29905
|
+
import { setClerkAuthTokenGetter } from "@/utils/clerk-auth";
|
|
29906
|
+
{{/if}}
|
|
27883
29907
|
{{#if (and (eq backend "convex") (eq auth "clerk"))}}
|
|
27884
|
-
import { ClerkProvider, useAuth } from "@clerk/tanstack-react-start";
|
|
27885
29908
|
import { auth } from "@clerk/tanstack-react-start/server";
|
|
27886
29909
|
import { createServerFn } from "@tanstack/react-start";
|
|
27887
29910
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
@@ -27904,6 +29927,22 @@ const getAuth = createServerFn({ method: "GET" }).handler(async () => {
|
|
|
27904
29927
|
import { ConvexProvider } from "convex/react";
|
|
27905
29928
|
{{/if}}
|
|
27906
29929
|
|
|
29930
|
+
{{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
|
|
29931
|
+
function ClerkApiAuthBridge() {
|
|
29932
|
+
const { getToken } = useAuth();
|
|
29933
|
+
|
|
29934
|
+
useEffect(() => {
|
|
29935
|
+
setClerkAuthTokenGetter(getToken);
|
|
29936
|
+
|
|
29937
|
+
return () => {
|
|
29938
|
+
setClerkAuthTokenGetter(null);
|
|
29939
|
+
};
|
|
29940
|
+
}, [getToken]);
|
|
29941
|
+
|
|
29942
|
+
return null;
|
|
29943
|
+
}
|
|
29944
|
+
{{/if}}
|
|
29945
|
+
|
|
27907
29946
|
{{#if (eq backend "convex")}}
|
|
27908
29947
|
export interface RouterAppContext {
|
|
27909
29948
|
queryClient: QueryClient;
|
|
@@ -28021,6 +30060,31 @@ function RootDocument() {
|
|
|
28021
30060
|
</html>
|
|
28022
30061
|
</ConvexBetterAuthProvider>
|
|
28023
30062
|
);
|
|
30063
|
+
{{else if (eq auth "clerk")}}
|
|
30064
|
+
return (
|
|
30065
|
+
<ClerkProvider>
|
|
30066
|
+
{{#unless (eq api "none")}}
|
|
30067
|
+
<ClerkApiAuthBridge />
|
|
30068
|
+
{{/unless}}
|
|
30069
|
+
<html lang="en" className="dark">
|
|
30070
|
+
<head>
|
|
30071
|
+
<HeadContent />
|
|
30072
|
+
</head>
|
|
30073
|
+
<body>
|
|
30074
|
+
<div className="grid h-svh grid-rows-[auto_1fr]">
|
|
30075
|
+
<Header />
|
|
30076
|
+
<Outlet />
|
|
30077
|
+
</div>
|
|
30078
|
+
<Toaster richColors />
|
|
30079
|
+
<TanStackRouterDevtools position="bottom-left" />
|
|
30080
|
+
{{#unless (eq api "none")}}
|
|
30081
|
+
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
|
|
30082
|
+
{{/unless}}
|
|
30083
|
+
<Scripts />
|
|
30084
|
+
</body>
|
|
30085
|
+
</html>
|
|
30086
|
+
</ClerkProvider>
|
|
30087
|
+
);
|
|
28024
30088
|
{{else if (eq backend "convex")}}
|
|
28025
30089
|
const { convexQueryClient } = Route.useRouteContext();
|
|
28026
30090
|
return (
|
|
@@ -29075,16 +31139,17 @@ export const env = createEnv({
|
|
|
29075
31139
|
{{#if (eq auth "better-auth")}}
|
|
29076
31140
|
EXPO_PUBLIC_CONVEX_SITE_URL: z.url(),
|
|
29077
31141
|
{{/if}}
|
|
29078
|
-
{{#if (eq auth "clerk")}}
|
|
29079
|
-
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
29080
|
-
{{/if}}
|
|
29081
31142
|
{{else}}
|
|
29082
31143
|
EXPO_PUBLIC_SERVER_URL: z.url(),
|
|
31144
|
+
{{/if}}
|
|
31145
|
+
{{#if (eq auth "clerk")}}
|
|
31146
|
+
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
29083
31147
|
{{/if}}
|
|
29084
31148
|
},
|
|
29085
31149
|
runtimeEnv: process.env,
|
|
29086
31150
|
emptyStringAsUndefined: true,
|
|
29087
|
-
})
|
|
31151
|
+
});
|
|
31152
|
+
`],
|
|
29088
31153
|
["packages/env/src/server.ts.hbs", `{{#if (or (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}
|
|
29089
31154
|
/// <reference path="../env.d.ts" />
|
|
29090
31155
|
// For Cloudflare Workers, env is accessed via cloudflare:workers module
|
|
@@ -29113,6 +31178,12 @@ export const env = createEnv({
|
|
|
29113
31178
|
BETTER_AUTH_SECRET: z.string().min(32),
|
|
29114
31179
|
BETTER_AUTH_URL: z.url(),
|
|
29115
31180
|
{{/if}}
|
|
31181
|
+
{{#if (eq auth "clerk")}}
|
|
31182
|
+
CLERK_SECRET_KEY: z.string().min(1),
|
|
31183
|
+
{{#if (or (eq backend "express") (eq backend "fastify") (and (ne api "none") (or (eq backend "self") (eq backend "hono") (eq backend "elysia"))))}}
|
|
31184
|
+
CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
31185
|
+
{{/if}}
|
|
31186
|
+
{{/if}}
|
|
29116
31187
|
{{#if (eq payments "polar")}}
|
|
29117
31188
|
POLAR_ACCESS_TOKEN: z.string().min(1),
|
|
29118
31189
|
POLAR_SUCCESS_URL: z.url(),
|
|
@@ -29190,22 +31261,40 @@ export const env = createEnv({
|
|
|
29190
31261
|
{{/if}}
|
|
29191
31262
|
{{else if (eq backend "self")}}
|
|
29192
31263
|
{{#if (includes frontend "next")}}
|
|
29193
|
-
client: {
|
|
29194
|
-
|
|
31264
|
+
client: {
|
|
31265
|
+
{{#if (eq auth "clerk")}}
|
|
31266
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
31267
|
+
{{/if}}
|
|
31268
|
+
},
|
|
31269
|
+
runtimeEnv: {
|
|
31270
|
+
{{#if (eq auth "clerk")}}
|
|
31271
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
|
|
31272
|
+
{{/if}}
|
|
31273
|
+
},
|
|
29195
31274
|
{{else if (includes frontend "nuxt")}}
|
|
29196
31275
|
client: {},
|
|
29197
31276
|
{{else}}
|
|
29198
31277
|
clientPrefix: "VITE_",
|
|
29199
|
-
client: {
|
|
31278
|
+
client: {
|
|
31279
|
+
{{#if (eq auth "clerk")}}
|
|
31280
|
+
VITE_CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
31281
|
+
{{/if}}
|
|
31282
|
+
},
|
|
29200
31283
|
runtimeEnv: (import.meta as any).env,
|
|
29201
31284
|
{{/if}}
|
|
29202
31285
|
{{else if (ne backend "none")}}
|
|
29203
31286
|
{{#if (includes frontend "next")}}
|
|
29204
31287
|
client: {
|
|
29205
31288
|
NEXT_PUBLIC_SERVER_URL: z.url(),
|
|
31289
|
+
{{#if (eq auth "clerk")}}
|
|
31290
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
31291
|
+
{{/if}}
|
|
29206
31292
|
},
|
|
29207
31293
|
runtimeEnv: {
|
|
29208
31294
|
NEXT_PUBLIC_SERVER_URL: process.env.NEXT_PUBLIC_SERVER_URL,
|
|
31295
|
+
{{#if (eq auth "clerk")}}
|
|
31296
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
|
|
31297
|
+
{{/if}}
|
|
29209
31298
|
},
|
|
29210
31299
|
{{else if (includes frontend "nuxt")}}
|
|
29211
31300
|
client: {
|
|
@@ -29221,12 +31310,16 @@ export const env = createEnv({
|
|
|
29221
31310
|
clientPrefix: "VITE_",
|
|
29222
31311
|
client: {
|
|
29223
31312
|
VITE_SERVER_URL: z.url(),
|
|
31313
|
+
{{#if (eq auth "clerk")}}
|
|
31314
|
+
VITE_CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
31315
|
+
{{/if}}
|
|
29224
31316
|
},
|
|
29225
31317
|
runtimeEnv: (import.meta as any).env,
|
|
29226
31318
|
{{/if}}
|
|
29227
31319
|
{{/if}}
|
|
29228
31320
|
emptyStringAsUndefined: true,
|
|
29229
|
-
})
|
|
31321
|
+
});
|
|
31322
|
+
`],
|
|
29230
31323
|
["packages/env/tsconfig.json.hbs", `{
|
|
29231
31324
|
"extends": "@{{projectName}}/config/tsconfig.base.json",
|
|
29232
31325
|
}
|
|
@@ -29310,6 +31403,9 @@ export const web = await Nextjs("web", {
|
|
|
29310
31403
|
{{/if}}
|
|
29311
31404
|
{{#if (eq auth "clerk")}}
|
|
29312
31405
|
CLERK_SECRET_KEY: alchemy.secret.env.CLERK_SECRET_KEY!,
|
|
31406
|
+
{{#if (and (ne api "none") (or (eq backend "self") (eq backend "hono") (eq backend "elysia")))}}
|
|
31407
|
+
CLERK_PUBLISHABLE_KEY: alchemy.env.CLERK_PUBLISHABLE_KEY!,
|
|
31408
|
+
{{/if}}
|
|
29313
31409
|
{{/if}}
|
|
29314
31410
|
{{#if (and (includes examples "ai") (ne backend "convex"))}}
|
|
29315
31411
|
GOOGLE_GENERATIVE_AI_API_KEY: alchemy.secret.env.GOOGLE_GENERATIVE_AI_API_KEY!,
|
|
@@ -29362,6 +31458,9 @@ export const web = await Nuxt("web", {
|
|
|
29362
31458
|
{{/if}}
|
|
29363
31459
|
{{#if (eq auth "clerk")}}
|
|
29364
31460
|
CLERK_SECRET_KEY: alchemy.secret.env.CLERK_SECRET_KEY!,
|
|
31461
|
+
{{#if (and (ne api "none") (or (eq backend "self") (eq backend "hono") (eq backend "elysia")))}}
|
|
31462
|
+
CLERK_PUBLISHABLE_KEY: alchemy.env.CLERK_PUBLISHABLE_KEY!,
|
|
31463
|
+
{{/if}}
|
|
29365
31464
|
{{/if}}
|
|
29366
31465
|
{{#if (and (includes examples "ai") (ne backend "convex"))}}
|
|
29367
31466
|
GOOGLE_GENERATIVE_AI_API_KEY: alchemy.secret.env.GOOGLE_GENERATIVE_AI_API_KEY!,
|
|
@@ -29423,6 +31522,9 @@ export const web = await TanStackStart("web", {
|
|
|
29423
31522
|
{{/if}}
|
|
29424
31523
|
{{#if (eq auth "clerk")}}
|
|
29425
31524
|
CLERK_SECRET_KEY: alchemy.secret.env.CLERK_SECRET_KEY!,
|
|
31525
|
+
{{#if (and (ne api "none") (or (eq backend "self") (eq backend "hono") (eq backend "elysia")))}}
|
|
31526
|
+
CLERK_PUBLISHABLE_KEY: alchemy.env.CLERK_PUBLISHABLE_KEY!,
|
|
31527
|
+
{{/if}}
|
|
29426
31528
|
{{/if}}
|
|
29427
31529
|
{{#if (and (includes examples "ai") (ne backend "convex"))}}
|
|
29428
31530
|
GOOGLE_GENERATIVE_AI_API_KEY: alchemy.secret.env.GOOGLE_GENERATIVE_AI_API_KEY!,
|
|
@@ -29548,6 +31650,9 @@ export const server = await Worker("server", {
|
|
|
29548
31650
|
{{/if}}
|
|
29549
31651
|
{{#if (eq auth "clerk")}}
|
|
29550
31652
|
CLERK_SECRET_KEY: alchemy.secret.env.CLERK_SECRET_KEY!,
|
|
31653
|
+
{{#if (and (ne api "none") (or (eq backend "self") (eq backend "hono") (eq backend "elysia")))}}
|
|
31654
|
+
CLERK_PUBLISHABLE_KEY: alchemy.env.CLERK_PUBLISHABLE_KEY!,
|
|
31655
|
+
{{/if}}
|
|
29551
31656
|
{{/if}}
|
|
29552
31657
|
{{#if (includes examples "ai")}}
|
|
29553
31658
|
GOOGLE_GENERATIVE_AI_API_KEY: alchemy.secret.env.GOOGLE_GENERATIVE_AI_API_KEY!,
|
|
@@ -30526,7 +32631,7 @@ function SuccessPage() {
|
|
|
30526
32631
|
</div>
|
|
30527
32632
|
`]
|
|
30528
32633
|
]);
|
|
30529
|
-
const TEMPLATE_COUNT =
|
|
32634
|
+
const TEMPLATE_COUNT = 457;
|
|
30530
32635
|
|
|
30531
32636
|
//#endregion
|
|
30532
32637
|
export { EMBEDDED_TEMPLATES, GeneratorError, Handlebars, TEMPLATE_COUNT, VirtualFileSystem, dependencyVersionMap, generate, generateReproducibleCommand, isBinaryFile, processAddonTemplates, processAddonsDeps, processFileContent, processTemplateString, transformFilename, writeBtsConfigToVfs };
|