create-better-t-stack 2.18.5 → 2.19.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.js +118 -46
- package/package.json +1 -1
- package/templates/auth/server/base/src/lib/auth.ts.hbs +53 -20
- package/templates/backend/server/hono/src/index.ts.hbs +48 -14
- package/templates/backend/server/server-base/tsconfig.json.hbs +34 -30
- package/templates/db/drizzle/mysql/src/db/index.ts.hbs +20 -0
- package/templates/db/drizzle/postgres/src/db/index.ts.hbs +12 -0
- package/templates/db/drizzle/sqlite/{drizzle.config.ts → drizzle.config.ts.hbs} +2 -0
- package/templates/db/drizzle/sqlite/src/db/index.ts.hbs +28 -0
- package/templates/db/prisma/postgres/{prisma.config.ts → prisma.config.ts.hbs} +4 -0
- package/templates/runtime/workers/apps/server/wrangler.toml.hbs +18 -0
- package/templates/db/drizzle/mysql/src/db/index.ts +0 -3
- package/templates/db/drizzle/postgres/src/db/index.ts +0 -3
- package/templates/db/drizzle/sqlite/src/db/index.ts +0 -9
- /package/templates/db/drizzle/mysql/{drizzle.config.ts → drizzle.config.ts.hbs} +0 -0
- /package/templates/db/drizzle/postgres/{drizzle.config.ts → drizzle.config.ts.hbs} +0 -0
- /package/templates/db/mongoose/mongodb/src/db/{index.ts → index.ts.hbs} +0 -0
- /package/templates/db/prisma/mongodb/prisma/{index.ts → index.ts.hbs} +0 -0
- /package/templates/db/prisma/mongodb/{prisma.config.ts → prisma.config.ts.hbs} +0 -0
package/dist/index.js
CHANGED
|
@@ -99,7 +99,8 @@ const dependencyVersionMap = {
|
|
|
99
99
|
"@tanstack/react-query-devtools": "^5.80.5",
|
|
100
100
|
"@tanstack/react-query": "^5.80.5",
|
|
101
101
|
"@tanstack/solid-query": "^5.75.0",
|
|
102
|
-
"@tanstack/solid-query-devtools": "^5.75.0"
|
|
102
|
+
"@tanstack/solid-query-devtools": "^5.75.0",
|
|
103
|
+
wrangler: "^4.20.0"
|
|
103
104
|
};
|
|
104
105
|
|
|
105
106
|
//#endregion
|
|
@@ -539,7 +540,8 @@ async function setupAuth(config) {
|
|
|
539
540
|
"tanstack-start",
|
|
540
541
|
"next",
|
|
541
542
|
"nuxt",
|
|
542
|
-
"svelte"
|
|
543
|
+
"svelte",
|
|
544
|
+
"solid"
|
|
543
545
|
].includes(f));
|
|
544
546
|
if (hasWebFrontend && clientDirExists) await addPackageDependency({
|
|
545
547
|
dependencies: ["better-auth"],
|
|
@@ -740,7 +742,8 @@ async function setupEnvironmentVariables(config) {
|
|
|
740
742
|
databaseUrl = "mongodb://localhost:27017/mydatabase";
|
|
741
743
|
break;
|
|
742
744
|
case "sqlite":
|
|
743
|
-
databaseUrl = "
|
|
745
|
+
if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
|
|
746
|
+
else databaseUrl = "file:./local.db";
|
|
744
747
|
break;
|
|
745
748
|
}
|
|
746
749
|
const serverVars = [
|
|
@@ -771,6 +774,12 @@ async function setupEnvironmentVariables(config) {
|
|
|
771
774
|
}
|
|
772
775
|
];
|
|
773
776
|
await addEnvVariablesToFile(envPath, serverVars);
|
|
777
|
+
if (config.runtime === "workers") {
|
|
778
|
+
const devVarsPath = path.join(serverDir, ".dev.vars");
|
|
779
|
+
try {
|
|
780
|
+
await fs.copy(envPath, devVarsPath);
|
|
781
|
+
} catch (_err) {}
|
|
782
|
+
}
|
|
774
783
|
}
|
|
775
784
|
|
|
776
785
|
//#endregion
|
|
@@ -1585,6 +1594,7 @@ async function setupRuntime(config) {
|
|
|
1585
1594
|
if (!await fs.pathExists(serverDir)) return;
|
|
1586
1595
|
if (runtime === "bun") await setupBunRuntime(serverDir, backend);
|
|
1587
1596
|
else if (runtime === "node") await setupNodeRuntime(serverDir, backend);
|
|
1597
|
+
else if (runtime === "workers") await setupWorkersRuntime(serverDir);
|
|
1588
1598
|
}
|
|
1589
1599
|
async function setupBunRuntime(serverDir, _backend) {
|
|
1590
1600
|
const packageJsonPath = path.join(serverDir, "package.json");
|
|
@@ -1624,6 +1634,24 @@ async function setupNodeRuntime(serverDir, backend) {
|
|
|
1624
1634
|
projectDir: serverDir
|
|
1625
1635
|
});
|
|
1626
1636
|
}
|
|
1637
|
+
async function setupWorkersRuntime(serverDir) {
|
|
1638
|
+
const packageJsonPath = path.join(serverDir, "package.json");
|
|
1639
|
+
if (!await fs.pathExists(packageJsonPath)) return;
|
|
1640
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
1641
|
+
packageJson.scripts = {
|
|
1642
|
+
...packageJson.scripts,
|
|
1643
|
+
dev: "wrangler dev --port=3000",
|
|
1644
|
+
start: "wrangler dev",
|
|
1645
|
+
deploy: "wrangler deploy",
|
|
1646
|
+
build: "wrangler deploy --dry-run",
|
|
1647
|
+
"cf-typegen": "wrangler types --env-interface CloudflareBindings"
|
|
1648
|
+
};
|
|
1649
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
1650
|
+
await addPackageDependency({
|
|
1651
|
+
devDependencies: ["wrangler"],
|
|
1652
|
+
projectDir: serverDir
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1627
1655
|
|
|
1628
1656
|
//#endregion
|
|
1629
1657
|
//#region src/helpers/project-generation/create-readme.ts
|
|
@@ -1872,7 +1900,13 @@ function displayPostInstallInstructions(config) {
|
|
|
1872
1900
|
if (isConvex) {
|
|
1873
1901
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup ${pc.dim("(this will guide you through Convex project setup)")}\n`;
|
|
1874
1902
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
|
|
1875
|
-
} else
|
|
1903
|
+
} else {
|
|
1904
|
+
if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
1905
|
+
if (runtime === "workers") {
|
|
1906
|
+
output += `${pc.cyan(`${stepCounter++}.`)} bun dev\n`;
|
|
1907
|
+
output += `${pc.cyan(`${stepCounter++}.`)} cd apps/server && bun run cf-typegen\n\n`;
|
|
1908
|
+
} else output += "\n";
|
|
1909
|
+
}
|
|
1876
1910
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
1877
1911
|
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
1878
1912
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)\n`;
|
|
@@ -2127,8 +2161,9 @@ async function processTemplate(srcPath, destPath, context) {
|
|
|
2127
2161
|
throw new Error(`Failed to process template ${srcPath}`);
|
|
2128
2162
|
}
|
|
2129
2163
|
}
|
|
2130
|
-
handlebars.registerHelper("or", (a, b) => a || b);
|
|
2131
2164
|
handlebars.registerHelper("eq", (a, b) => a === b);
|
|
2165
|
+
handlebars.registerHelper("and", (a, b) => a && b);
|
|
2166
|
+
handlebars.registerHelper("or", (a, b) => a || b);
|
|
2132
2167
|
handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
|
|
2133
2168
|
|
|
2134
2169
|
//#endregion
|
|
@@ -2467,6 +2502,10 @@ async function handleExtras(projectDir, context) {
|
|
|
2467
2502
|
const npmrcDest = path.join(projectDir, ".npmrc");
|
|
2468
2503
|
if (await fs.pathExists(npmrcTemplateSrc)) await processTemplate(npmrcTemplateSrc, npmrcDest, context);
|
|
2469
2504
|
}
|
|
2505
|
+
if (context.runtime === "workers") {
|
|
2506
|
+
const runtimeWorkersDir = path.join(PKG_ROOT, "templates/runtime/workers");
|
|
2507
|
+
if (await fs.pathExists(runtimeWorkersDir)) await processAndCopyFiles("**/*", runtimeWorkersDir, projectDir, context, false);
|
|
2508
|
+
}
|
|
2470
2509
|
}
|
|
2471
2510
|
|
|
2472
2511
|
//#endregion
|
|
@@ -2701,38 +2740,39 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
|
2701
2740
|
|
|
2702
2741
|
//#endregion
|
|
2703
2742
|
//#region src/prompts/database.ts
|
|
2704
|
-
async function getDatabaseChoice(database, backend) {
|
|
2743
|
+
async function getDatabaseChoice(database, backend, runtime) {
|
|
2705
2744
|
if (backend === "convex" || backend === "none") return "none";
|
|
2706
2745
|
if (database !== void 0) return database;
|
|
2746
|
+
const databaseOptions = [
|
|
2747
|
+
{
|
|
2748
|
+
value: "none",
|
|
2749
|
+
label: "None",
|
|
2750
|
+
hint: "No database setup"
|
|
2751
|
+
},
|
|
2752
|
+
{
|
|
2753
|
+
value: "sqlite",
|
|
2754
|
+
label: "SQLite",
|
|
2755
|
+
hint: "lightweight, server-less, embedded relational database"
|
|
2756
|
+
},
|
|
2757
|
+
{
|
|
2758
|
+
value: "postgres",
|
|
2759
|
+
label: "PostgreSQL",
|
|
2760
|
+
hint: "powerful, open source object-relational database system"
|
|
2761
|
+
},
|
|
2762
|
+
{
|
|
2763
|
+
value: "mysql",
|
|
2764
|
+
label: "MySQL",
|
|
2765
|
+
hint: "popular open-source relational database system"
|
|
2766
|
+
}
|
|
2767
|
+
];
|
|
2768
|
+
if (runtime !== "workers") databaseOptions.push({
|
|
2769
|
+
value: "mongodb",
|
|
2770
|
+
label: "MongoDB",
|
|
2771
|
+
hint: "open-source NoSQL database that stores data in JSON-like documents called BSON"
|
|
2772
|
+
});
|
|
2707
2773
|
const response = await select({
|
|
2708
2774
|
message: "Select database",
|
|
2709
|
-
options:
|
|
2710
|
-
{
|
|
2711
|
-
value: "none",
|
|
2712
|
-
label: "None",
|
|
2713
|
-
hint: "No database setup"
|
|
2714
|
-
},
|
|
2715
|
-
{
|
|
2716
|
-
value: "sqlite",
|
|
2717
|
-
label: "SQLite",
|
|
2718
|
-
hint: "lightweight, server-less, embedded relational database"
|
|
2719
|
-
},
|
|
2720
|
-
{
|
|
2721
|
-
value: "postgres",
|
|
2722
|
-
label: "PostgreSQL",
|
|
2723
|
-
hint: "powerful, open source object-relational database system"
|
|
2724
|
-
},
|
|
2725
|
-
{
|
|
2726
|
-
value: "mysql",
|
|
2727
|
-
label: "MySQL",
|
|
2728
|
-
hint: "popular open-source relational database system"
|
|
2729
|
-
},
|
|
2730
|
-
{
|
|
2731
|
-
value: "mongodb",
|
|
2732
|
-
label: "MongoDB",
|
|
2733
|
-
hint: "open-source NoSQL database that stores data in JSON-like documents called BSON"
|
|
2734
|
-
}
|
|
2735
|
-
],
|
|
2775
|
+
options: databaseOptions,
|
|
2736
2776
|
initialValue: DEFAULT_CONFIG.database
|
|
2737
2777
|
});
|
|
2738
2778
|
if (isCancel(response)) {
|
|
@@ -2985,10 +3025,11 @@ const ormOptions = {
|
|
|
2985
3025
|
hint: "Lightweight and performant TypeScript ORM"
|
|
2986
3026
|
}
|
|
2987
3027
|
};
|
|
2988
|
-
async function getORMChoice(orm, hasDatabase, database, backend) {
|
|
3028
|
+
async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
2989
3029
|
if (backend === "convex") return "none";
|
|
2990
3030
|
if (!hasDatabase) return "none";
|
|
2991
3031
|
if (orm !== void 0) return orm;
|
|
3032
|
+
if (runtime === "workers") return "drizzle";
|
|
2992
3033
|
const options = [...database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma]];
|
|
2993
3034
|
const response = await select({
|
|
2994
3035
|
message: "Select ORM",
|
|
@@ -3041,17 +3082,23 @@ async function getRuntimeChoice(runtime, backend) {
|
|
|
3041
3082
|
if (backend === "convex" || backend === "none") return "none";
|
|
3042
3083
|
if (runtime !== void 0) return runtime;
|
|
3043
3084
|
if (backend === "next") return "node";
|
|
3085
|
+
const runtimeOptions = [{
|
|
3086
|
+
value: "bun",
|
|
3087
|
+
label: "Bun",
|
|
3088
|
+
hint: "Fast all-in-one JavaScript runtime"
|
|
3089
|
+
}, {
|
|
3090
|
+
value: "node",
|
|
3091
|
+
label: "Node.js",
|
|
3092
|
+
hint: "Traditional Node.js runtime"
|
|
3093
|
+
}];
|
|
3094
|
+
if (backend === "hono") runtimeOptions.push({
|
|
3095
|
+
value: "workers",
|
|
3096
|
+
label: "Cloudflare Workers (beta)",
|
|
3097
|
+
hint: "Edge runtime on Cloudflare's global network"
|
|
3098
|
+
});
|
|
3044
3099
|
const response = await select({
|
|
3045
3100
|
message: "Select runtime",
|
|
3046
|
-
options:
|
|
3047
|
-
value: "bun",
|
|
3048
|
-
label: "Bun",
|
|
3049
|
-
hint: "Fast all-in-one JavaScript runtime"
|
|
3050
|
-
}, {
|
|
3051
|
-
value: "node",
|
|
3052
|
-
label: "Node.js",
|
|
3053
|
-
hint: "Traditional Node.js runtime"
|
|
3054
|
-
}],
|
|
3101
|
+
options: runtimeOptions,
|
|
3055
3102
|
initialValue: DEFAULT_CONFIG.runtime
|
|
3056
3103
|
});
|
|
3057
3104
|
if (isCancel(response)) {
|
|
@@ -3068,8 +3115,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
3068
3115
|
frontend: () => getFrontendChoice(flags.frontend, flags.backend),
|
|
3069
3116
|
backend: ({ results }) => getBackendFrameworkChoice(flags.backend, results.frontend),
|
|
3070
3117
|
runtime: ({ results }) => getRuntimeChoice(flags.runtime, results.backend),
|
|
3071
|
-
database: ({ results }) => getDatabaseChoice(flags.database, results.backend),
|
|
3072
|
-
orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none", results.database, results.backend),
|
|
3118
|
+
database: ({ results }) => getDatabaseChoice(flags.database, results.backend, results.runtime),
|
|
3119
|
+
orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none", results.database, results.backend, results.runtime),
|
|
3073
3120
|
api: ({ results }) => getApiChoice(flags.api, results.frontend, results.backend),
|
|
3074
3121
|
auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend),
|
|
3075
3122
|
addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend),
|
|
@@ -3147,8 +3194,9 @@ const BackendSchema = z.enum([
|
|
|
3147
3194
|
const RuntimeSchema = z.enum([
|
|
3148
3195
|
"bun",
|
|
3149
3196
|
"node",
|
|
3197
|
+
"workers",
|
|
3150
3198
|
"none"
|
|
3151
|
-
]).describe("Runtime environment");
|
|
3199
|
+
]).describe("Runtime environment (workers only available with hono backend and drizzle orm)");
|
|
3152
3200
|
const FrontendSchema = z.enum([
|
|
3153
3201
|
"tanstack-router",
|
|
3154
3202
|
"react-router",
|
|
@@ -3654,6 +3702,30 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
3654
3702
|
consola$1.fatal("Supabase setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup.");
|
|
3655
3703
|
process.exit(1);
|
|
3656
3704
|
}
|
|
3705
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") {
|
|
3706
|
+
consola$1.fatal(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
|
|
3707
|
+
process.exit(1);
|
|
3708
|
+
}
|
|
3709
|
+
if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") {
|
|
3710
|
+
consola$1.fatal(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
|
|
3711
|
+
process.exit(1);
|
|
3712
|
+
}
|
|
3713
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.orm && config.orm !== "drizzle" && config.orm !== "none") {
|
|
3714
|
+
consola$1.fatal(`Cloudflare Workers runtime (--runtime workers) is only supported with Drizzle ORM (--orm drizzle) or no ORM (--orm none). Current ORM: ${config.orm}. Please use '--orm drizzle', '--orm none', or choose a different runtime.`);
|
|
3715
|
+
process.exit(1);
|
|
3716
|
+
}
|
|
3717
|
+
if (providedFlags.has("orm") && config.orm && config.orm !== "drizzle" && config.orm !== "none" && config.runtime === "workers") {
|
|
3718
|
+
consola$1.fatal(`ORM '${config.orm}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Drizzle ORM or no ORM. Please use '--orm drizzle', '--orm none', or choose a different runtime.`);
|
|
3719
|
+
process.exit(1);
|
|
3720
|
+
}
|
|
3721
|
+
if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") {
|
|
3722
|
+
consola$1.fatal("Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
|
|
3723
|
+
process.exit(1);
|
|
3724
|
+
}
|
|
3725
|
+
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") {
|
|
3726
|
+
consola$1.fatal("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
|
|
3727
|
+
process.exit(1);
|
|
3728
|
+
}
|
|
3657
3729
|
return config;
|
|
3658
3730
|
}
|
|
3659
3731
|
function getProvidedFlags(options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,21 +14,22 @@ export const auth = betterAuth({
|
|
|
14
14
|
{{#if (eq database "mongodb")}}provider: "mongodb"{{/if}}
|
|
15
15
|
}),
|
|
16
16
|
trustedOrigins: [
|
|
17
|
-
process.env.CORS_ORIGIN || "",
|
|
18
|
-
"
|
|
17
|
+
process.env.CORS_ORIGIN || "",
|
|
18
|
+
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
19
|
+
"my-better-t-app://",
|
|
20
|
+
{{/if}}
|
|
19
21
|
],
|
|
20
22
|
emailAndPassword: {
|
|
21
23
|
enabled: true,
|
|
22
24
|
}
|
|
23
|
-
|
|
24
25
|
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
25
|
-
,
|
|
26
|
-
plugins: [expo()]
|
|
26
|
+
, plugins: [expo()]
|
|
27
27
|
{{/if}}
|
|
28
28
|
});
|
|
29
29
|
{{/if}}
|
|
30
30
|
|
|
31
31
|
{{#if (eq orm "drizzle")}}
|
|
32
|
+
{{#if (or (eq runtime "bun") (eq runtime "node"))}}
|
|
32
33
|
import { betterAuth } from "better-auth";
|
|
33
34
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
34
35
|
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
@@ -45,19 +46,51 @@ export const auth = betterAuth({
|
|
|
45
46
|
schema: schema,
|
|
46
47
|
}),
|
|
47
48
|
trustedOrigins: [
|
|
48
|
-
process.env.CORS_ORIGIN || "",
|
|
49
|
-
"
|
|
49
|
+
process.env.CORS_ORIGIN || "",
|
|
50
|
+
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
51
|
+
"my-better-t-app://",
|
|
52
|
+
{{/if}}
|
|
50
53
|
],
|
|
51
54
|
emailAndPassword: {
|
|
52
55
|
enabled: true,
|
|
53
|
-
}
|
|
56
|
+
},
|
|
57
|
+
secret: process.env.BETTER_AUTH_SECRET,
|
|
58
|
+
baseURL: process.env.BETTER_AUTH_URL,
|
|
59
|
+
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
60
|
+
plugins: [expo()],
|
|
61
|
+
{{/if}}
|
|
62
|
+
});
|
|
63
|
+
{{/if}}
|
|
64
|
+
|
|
65
|
+
{{#if (eq runtime "workers")}}
|
|
66
|
+
import { betterAuth } from "better-auth";
|
|
67
|
+
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
68
|
+
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
69
|
+
import { expo } from "@better-auth/expo";
|
|
70
|
+
{{/if}}
|
|
71
|
+
import { db } from "@/db";
|
|
72
|
+
import * as schema from "../db/schema/auth";
|
|
73
|
+
import { env } from "cloudflare:workers";
|
|
54
74
|
|
|
75
|
+
export const auth = betterAuth({
|
|
76
|
+
database: drizzleAdapter(db, {
|
|
77
|
+
{{#if (eq database "postgres")}}provider: "pg",{{/if}}
|
|
78
|
+
{{#if (eq database "sqlite")}}provider: "sqlite",{{/if}}
|
|
79
|
+
{{#if (eq database "mysql")}}provider: "mysql",{{/if}}
|
|
80
|
+
schema: schema,
|
|
81
|
+
}),
|
|
82
|
+
trustedOrigins: [env.CORS_ORIGIN],
|
|
83
|
+
emailAndPassword: {
|
|
84
|
+
enabled: true,
|
|
85
|
+
},
|
|
86
|
+
secret: env.BETTER_AUTH_SECRET,
|
|
87
|
+
baseURL: env.BETTER_AUTH_URL,
|
|
55
88
|
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
56
|
-
,
|
|
57
|
-
plugins: [expo()]
|
|
89
|
+
plugins: [expo()],
|
|
58
90
|
{{/if}}
|
|
59
91
|
});
|
|
60
92
|
{{/if}}
|
|
93
|
+
{{/if}}
|
|
61
94
|
|
|
62
95
|
{{#if (eq orm "mongoose")}}
|
|
63
96
|
import { betterAuth } from "better-auth";
|
|
@@ -70,16 +103,16 @@ import { client } from "../db";
|
|
|
70
103
|
export const auth = betterAuth({
|
|
71
104
|
database: mongodbAdapter(client),
|
|
72
105
|
trustedOrigins: [
|
|
73
|
-
process.env.CORS_ORIGIN || "",
|
|
74
|
-
"
|
|
106
|
+
process.env.CORS_ORIGIN || "",
|
|
107
|
+
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
108
|
+
"my-better-t-app://",
|
|
109
|
+
{{/if}}
|
|
75
110
|
],
|
|
76
111
|
emailAndPassword: {
|
|
77
112
|
enabled: true,
|
|
78
113
|
}
|
|
79
|
-
|
|
80
114
|
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
81
|
-
,
|
|
82
|
-
plugins: [expo()]
|
|
115
|
+
, plugins: [expo()]
|
|
83
116
|
{{/if}}
|
|
84
117
|
});
|
|
85
118
|
{{/if}}
|
|
@@ -93,16 +126,16 @@ import { expo } from "@better-auth/expo";
|
|
|
93
126
|
export const auth = betterAuth({
|
|
94
127
|
database: "", // Invalid configuration
|
|
95
128
|
trustedOrigins: [
|
|
96
|
-
process.env.CORS_ORIGIN || "",
|
|
97
|
-
"
|
|
129
|
+
process.env.CORS_ORIGIN || "",
|
|
130
|
+
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
131
|
+
"my-better-t-app://",
|
|
132
|
+
{{/if}}
|
|
98
133
|
],
|
|
99
134
|
emailAndPassword: {
|
|
100
135
|
enabled: true,
|
|
101
136
|
}
|
|
102
|
-
|
|
103
137
|
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
104
|
-
,
|
|
105
|
-
plugins: [expo()]
|
|
138
|
+
, plugins: [expo()]
|
|
106
139
|
{{/if}}
|
|
107
140
|
});
|
|
108
141
|
{{/if}}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
{{#if (or (eq runtime "bun") (eq runtime "node"))}}
|
|
1
2
|
import "dotenv/config";
|
|
3
|
+
{{/if}}
|
|
4
|
+
{{#if (eq runtime "workers")}}
|
|
5
|
+
import { env } from "cloudflare:workers";
|
|
6
|
+
{{/if}}
|
|
2
7
|
{{#if (eq api "orpc")}}
|
|
3
8
|
import { RPCHandler } from "@orpc/server/fetch";
|
|
4
9
|
import { createContext } from "./lib/context";
|
|
@@ -15,26 +20,33 @@ import { auth } from "./lib/auth";
|
|
|
15
20
|
import { Hono } from "hono";
|
|
16
21
|
import { cors } from "hono/cors";
|
|
17
22
|
import { logger } from "hono/logger";
|
|
18
|
-
{{#if (includes examples "ai")}}
|
|
23
|
+
{{#if (and (includes examples "ai") (or (eq runtime "bun") (eq runtime "node")))}}
|
|
19
24
|
import { streamText } from "ai";
|
|
20
25
|
import { google } from "@ai-sdk/google";
|
|
21
26
|
import { stream } from "hono/streaming";
|
|
22
27
|
{{/if}}
|
|
28
|
+
{{#if (and (includes examples "ai") (eq runtime "workers"))}}
|
|
29
|
+
import { streamText } from "ai";
|
|
30
|
+
import { stream } from "hono/streaming";
|
|
31
|
+
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
32
|
+
{{/if}}
|
|
23
33
|
|
|
24
34
|
const app = new Hono();
|
|
25
35
|
|
|
26
36
|
app.use(logger());
|
|
27
|
-
app.use(
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
app.use("/*", cors({
|
|
38
|
+
{{#if (or (eq runtime "bun") (eq runtime "node"))}}
|
|
39
|
+
origin: process.env.CORS_ORIGIN || "",
|
|
40
|
+
{{/if}}
|
|
41
|
+
{{#if (eq runtime "workers")}}
|
|
42
|
+
origin: env.CORS_ORIGIN || "",
|
|
43
|
+
{{/if}}
|
|
44
|
+
allowMethods: ["GET", "POST", "OPTIONS"],
|
|
45
|
+
{{#if auth}}
|
|
46
|
+
allowHeaders: ["Content-Type", "Authorization"],
|
|
47
|
+
credentials: true,
|
|
48
|
+
{{/if}}
|
|
49
|
+
}));
|
|
38
50
|
|
|
39
51
|
{{#if auth}}
|
|
40
52
|
app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
|
|
@@ -48,6 +60,7 @@ app.use("/rpc/*", async (c, next) => {
|
|
|
48
60
|
prefix: "/rpc",
|
|
49
61
|
context: context,
|
|
50
62
|
});
|
|
63
|
+
|
|
51
64
|
if (matched) {
|
|
52
65
|
return c.newResponse(response.body, response);
|
|
53
66
|
}
|
|
@@ -64,11 +77,10 @@ app.use("/trpc/*", trpcServer({
|
|
|
64
77
|
}));
|
|
65
78
|
{{/if}}
|
|
66
79
|
|
|
67
|
-
{{#if (includes examples "ai")}}
|
|
80
|
+
{{#if (and (includes examples "ai") (or (eq runtime "bun") (eq runtime "node")))}}
|
|
68
81
|
app.post("/ai", async (c) => {
|
|
69
82
|
const body = await c.req.json();
|
|
70
83
|
const messages = body.messages || [];
|
|
71
|
-
|
|
72
84
|
const result = streamText({
|
|
73
85
|
model: google("gemini-1.5-flash"),
|
|
74
86
|
messages,
|
|
@@ -76,7 +88,24 @@ app.post("/ai", async (c) => {
|
|
|
76
88
|
|
|
77
89
|
c.header("X-Vercel-AI-Data-Stream", "v1");
|
|
78
90
|
c.header("Content-Type", "text/plain; charset=utf-8");
|
|
91
|
+
return stream(c, (stream) => stream.pipe(result.toDataStream()));
|
|
92
|
+
});
|
|
93
|
+
{{/if}}
|
|
79
94
|
|
|
95
|
+
{{#if (and (includes examples "ai") (eq runtime "workers"))}}
|
|
96
|
+
app.post("/ai", async (c) => {
|
|
97
|
+
const body = await c.req.json();
|
|
98
|
+
const messages = body.messages || [];
|
|
99
|
+
const google = createGoogleGenerativeAI({
|
|
100
|
+
apiKey: env.GOOGLE_GENERATIVE_AI_API_KEY,
|
|
101
|
+
});
|
|
102
|
+
const result = streamText({
|
|
103
|
+
model: google("gemini-1.5-flash"),
|
|
104
|
+
messages,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
c.header("X-Vercel-AI-Data-Stream", "v1");
|
|
108
|
+
c.header("Content-Type", "text/plain; charset=utf-8");
|
|
80
109
|
return stream(c, (stream) => stream.pipe(result.toDataStream()));
|
|
81
110
|
});
|
|
82
111
|
{{/if}}
|
|
@@ -95,5 +124,10 @@ serve({
|
|
|
95
124
|
console.log(`Server is running on http://localhost:${info.port}`);
|
|
96
125
|
});
|
|
97
126
|
{{else}}
|
|
127
|
+
{{#if (eq runtime "bun")}}
|
|
128
|
+
export default app;
|
|
129
|
+
{{/if}}
|
|
130
|
+
{{#if (eq runtime "workers")}}
|
|
98
131
|
export default app;
|
|
132
|
+
{{/if}}
|
|
99
133
|
{{/if}}
|
|
@@ -1,35 +1,39 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
{{#if (eq runtime
|
|
19
|
-
|
|
20
|
-
{{else if (eq runtime
|
|
21
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"verbatimModuleSyntax": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"baseUrl": "./",
|
|
10
|
+
"paths": {
|
|
11
|
+
"@/*": ["./src/*"]
|
|
12
|
+
{{#if (eq orm "prisma")}},
|
|
13
|
+
"prisma": ["node_modules/prisma"]
|
|
14
|
+
{{/if}}
|
|
15
|
+
},
|
|
16
|
+
"outDir": "./dist",
|
|
17
|
+
"types": [
|
|
18
|
+
{{#if (eq runtime "node")}}
|
|
19
|
+
"node"
|
|
20
|
+
{{else if (eq runtime "bun")}}
|
|
21
|
+
"bun"
|
|
22
|
+
{{else if (eq runtime "workers")}}
|
|
23
|
+
"./worker-configuration",
|
|
24
|
+
"node"
|
|
22
25
|
{{else}}
|
|
23
|
-
|
|
26
|
+
"node",
|
|
27
|
+
"bun"
|
|
24
28
|
{{/if}}
|
|
25
29
|
],
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
{{#unless (or (eq backend "convex") (eq backend "none"))}}
|
|
31
|
+
"composite": true,
|
|
32
|
+
{{/unless}}
|
|
33
|
+
"jsx": "react-jsx"{{#if (eq backend "hono")}},
|
|
34
|
+
"jsxImportSource": "hono/jsx"{{/if}}
|
|
35
|
+
},
|
|
36
|
+
"tsc-alias": {
|
|
37
|
+
"resolveFullPaths": true
|
|
38
|
+
}
|
|
35
39
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{{#if (or (eq runtime "bun") (eq runtime "node"))}}
|
|
2
|
+
import { drizzle } from "drizzle-orm/mysql2";
|
|
3
|
+
|
|
4
|
+
export const db = drizzle({
|
|
5
|
+
connection: {
|
|
6
|
+
uri: process.env.DATABASE_URL,
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
{{/if}}
|
|
10
|
+
|
|
11
|
+
{{#if (eq runtime "workers")}}
|
|
12
|
+
import { drizzle } from "drizzle-orm/mysql2";
|
|
13
|
+
import { env } from "cloudflare:workers";
|
|
14
|
+
|
|
15
|
+
export const db = drizzle({
|
|
16
|
+
connection: {
|
|
17
|
+
uri: env.DATABASE_URL,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
{{/if}}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{{#if (or (eq runtime "bun") (eq runtime "node"))}}
|
|
2
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
3
|
+
|
|
4
|
+
export const db = drizzle(process.env.DATABASE_URL || "");
|
|
5
|
+
{{/if}}
|
|
6
|
+
|
|
7
|
+
{{#if (eq runtime "workers")}}
|
|
8
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
9
|
+
import { env } from "cloudflare:workers";
|
|
10
|
+
|
|
11
|
+
export const db = drizzle(env.DATABASE_URL || "");
|
|
12
|
+
{{/if}}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{{#if (or (eq runtime "bun") (eq runtime "node"))}}
|
|
2
|
+
import { drizzle } from "drizzle-orm/libsql";
|
|
3
|
+
import { createClient } from "@libsql/client";
|
|
4
|
+
|
|
5
|
+
const client = createClient({
|
|
6
|
+
url: process.env.DATABASE_URL || "",
|
|
7
|
+
{{#if (eq dbSetup "turso")}}
|
|
8
|
+
authToken: process.env.DATABASE_AUTH_TOKEN,
|
|
9
|
+
{{/if}}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const db = drizzle({ client });
|
|
13
|
+
{{/if}}
|
|
14
|
+
|
|
15
|
+
{{#if (eq runtime "workers")}}
|
|
16
|
+
import { drizzle } from "drizzle-orm/libsql";
|
|
17
|
+
import { env } from "cloudflare:workers";
|
|
18
|
+
import { createClient } from "@libsql/client";
|
|
19
|
+
|
|
20
|
+
const client = createClient({
|
|
21
|
+
url: env.DATABASE_URL || "",
|
|
22
|
+
{{#if (eq dbSetup "turso")}}
|
|
23
|
+
authToken: env.DATABASE_AUTH_TOKEN,
|
|
24
|
+
{{/if}}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const db = drizzle({ client });
|
|
28
|
+
{{/if}}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name = "{{projectName}}-server"
|
|
2
|
+
main = "src/index.ts"
|
|
3
|
+
compatibility_date = "2025-06-15"
|
|
4
|
+
compatibility_flags = ["nodejs_compat"]
|
|
5
|
+
|
|
6
|
+
[vars]
|
|
7
|
+
NODE_ENV = "production"
|
|
8
|
+
|
|
9
|
+
# Non-sensitive environment variables (visible in dashboard)
|
|
10
|
+
# CORS_ORIGIN = "https://your-frontend-domain.com"
|
|
11
|
+
# BETTER_AUTH_URL = "https://your-worker-domain.workers.dev"
|
|
12
|
+
|
|
13
|
+
# ⚠️ SENSITIVE DATA: Use `wrangler secret put` instead of adding here
|
|
14
|
+
# Don't put these in [vars] - they'll be visible in the dashboard!
|
|
15
|
+
# - DATABASE_URL
|
|
16
|
+
# - DATABASE_AUTH_TOKEN
|
|
17
|
+
# - GOOGLE_GENERATIVE_AI_API_KEY
|
|
18
|
+
# - BETTER_AUTH_SECRET
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|