create-better-t-stack 2.18.6 → 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 +116 -45
- 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
|
|
@@ -741,7 +742,8 @@ async function setupEnvironmentVariables(config) {
|
|
|
741
742
|
databaseUrl = "mongodb://localhost:27017/mydatabase";
|
|
742
743
|
break;
|
|
743
744
|
case "sqlite":
|
|
744
|
-
databaseUrl = "
|
|
745
|
+
if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
|
|
746
|
+
else databaseUrl = "file:./local.db";
|
|
745
747
|
break;
|
|
746
748
|
}
|
|
747
749
|
const serverVars = [
|
|
@@ -772,6 +774,12 @@ async function setupEnvironmentVariables(config) {
|
|
|
772
774
|
}
|
|
773
775
|
];
|
|
774
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
|
+
}
|
|
775
783
|
}
|
|
776
784
|
|
|
777
785
|
//#endregion
|
|
@@ -1586,6 +1594,7 @@ async function setupRuntime(config) {
|
|
|
1586
1594
|
if (!await fs.pathExists(serverDir)) return;
|
|
1587
1595
|
if (runtime === "bun") await setupBunRuntime(serverDir, backend);
|
|
1588
1596
|
else if (runtime === "node") await setupNodeRuntime(serverDir, backend);
|
|
1597
|
+
else if (runtime === "workers") await setupWorkersRuntime(serverDir);
|
|
1589
1598
|
}
|
|
1590
1599
|
async function setupBunRuntime(serverDir, _backend) {
|
|
1591
1600
|
const packageJsonPath = path.join(serverDir, "package.json");
|
|
@@ -1625,6 +1634,24 @@ async function setupNodeRuntime(serverDir, backend) {
|
|
|
1625
1634
|
projectDir: serverDir
|
|
1626
1635
|
});
|
|
1627
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
|
+
}
|
|
1628
1655
|
|
|
1629
1656
|
//#endregion
|
|
1630
1657
|
//#region src/helpers/project-generation/create-readme.ts
|
|
@@ -1873,7 +1900,13 @@ function displayPostInstallInstructions(config) {
|
|
|
1873
1900
|
if (isConvex) {
|
|
1874
1901
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup ${pc.dim("(this will guide you through Convex project setup)")}\n`;
|
|
1875
1902
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
|
|
1876
|
-
} 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
|
+
}
|
|
1877
1910
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
1878
1911
|
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
1879
1912
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)\n`;
|
|
@@ -2128,8 +2161,9 @@ async function processTemplate(srcPath, destPath, context) {
|
|
|
2128
2161
|
throw new Error(`Failed to process template ${srcPath}`);
|
|
2129
2162
|
}
|
|
2130
2163
|
}
|
|
2131
|
-
handlebars.registerHelper("or", (a, b) => a || b);
|
|
2132
2164
|
handlebars.registerHelper("eq", (a, b) => a === b);
|
|
2165
|
+
handlebars.registerHelper("and", (a, b) => a && b);
|
|
2166
|
+
handlebars.registerHelper("or", (a, b) => a || b);
|
|
2133
2167
|
handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
|
|
2134
2168
|
|
|
2135
2169
|
//#endregion
|
|
@@ -2468,6 +2502,10 @@ async function handleExtras(projectDir, context) {
|
|
|
2468
2502
|
const npmrcDest = path.join(projectDir, ".npmrc");
|
|
2469
2503
|
if (await fs.pathExists(npmrcTemplateSrc)) await processTemplate(npmrcTemplateSrc, npmrcDest, context);
|
|
2470
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
|
+
}
|
|
2471
2509
|
}
|
|
2472
2510
|
|
|
2473
2511
|
//#endregion
|
|
@@ -2702,38 +2740,39 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
|
2702
2740
|
|
|
2703
2741
|
//#endregion
|
|
2704
2742
|
//#region src/prompts/database.ts
|
|
2705
|
-
async function getDatabaseChoice(database, backend) {
|
|
2743
|
+
async function getDatabaseChoice(database, backend, runtime) {
|
|
2706
2744
|
if (backend === "convex" || backend === "none") return "none";
|
|
2707
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
|
+
});
|
|
2708
2773
|
const response = await select({
|
|
2709
2774
|
message: "Select database",
|
|
2710
|
-
options:
|
|
2711
|
-
{
|
|
2712
|
-
value: "none",
|
|
2713
|
-
label: "None",
|
|
2714
|
-
hint: "No database setup"
|
|
2715
|
-
},
|
|
2716
|
-
{
|
|
2717
|
-
value: "sqlite",
|
|
2718
|
-
label: "SQLite",
|
|
2719
|
-
hint: "lightweight, server-less, embedded relational database"
|
|
2720
|
-
},
|
|
2721
|
-
{
|
|
2722
|
-
value: "postgres",
|
|
2723
|
-
label: "PostgreSQL",
|
|
2724
|
-
hint: "powerful, open source object-relational database system"
|
|
2725
|
-
},
|
|
2726
|
-
{
|
|
2727
|
-
value: "mysql",
|
|
2728
|
-
label: "MySQL",
|
|
2729
|
-
hint: "popular open-source relational database system"
|
|
2730
|
-
},
|
|
2731
|
-
{
|
|
2732
|
-
value: "mongodb",
|
|
2733
|
-
label: "MongoDB",
|
|
2734
|
-
hint: "open-source NoSQL database that stores data in JSON-like documents called BSON"
|
|
2735
|
-
}
|
|
2736
|
-
],
|
|
2775
|
+
options: databaseOptions,
|
|
2737
2776
|
initialValue: DEFAULT_CONFIG.database
|
|
2738
2777
|
});
|
|
2739
2778
|
if (isCancel(response)) {
|
|
@@ -2986,10 +3025,11 @@ const ormOptions = {
|
|
|
2986
3025
|
hint: "Lightweight and performant TypeScript ORM"
|
|
2987
3026
|
}
|
|
2988
3027
|
};
|
|
2989
|
-
async function getORMChoice(orm, hasDatabase, database, backend) {
|
|
3028
|
+
async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
2990
3029
|
if (backend === "convex") return "none";
|
|
2991
3030
|
if (!hasDatabase) return "none";
|
|
2992
3031
|
if (orm !== void 0) return orm;
|
|
3032
|
+
if (runtime === "workers") return "drizzle";
|
|
2993
3033
|
const options = [...database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma]];
|
|
2994
3034
|
const response = await select({
|
|
2995
3035
|
message: "Select ORM",
|
|
@@ -3042,17 +3082,23 @@ async function getRuntimeChoice(runtime, backend) {
|
|
|
3042
3082
|
if (backend === "convex" || backend === "none") return "none";
|
|
3043
3083
|
if (runtime !== void 0) return runtime;
|
|
3044
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
|
+
});
|
|
3045
3099
|
const response = await select({
|
|
3046
3100
|
message: "Select runtime",
|
|
3047
|
-
options:
|
|
3048
|
-
value: "bun",
|
|
3049
|
-
label: "Bun",
|
|
3050
|
-
hint: "Fast all-in-one JavaScript runtime"
|
|
3051
|
-
}, {
|
|
3052
|
-
value: "node",
|
|
3053
|
-
label: "Node.js",
|
|
3054
|
-
hint: "Traditional Node.js runtime"
|
|
3055
|
-
}],
|
|
3101
|
+
options: runtimeOptions,
|
|
3056
3102
|
initialValue: DEFAULT_CONFIG.runtime
|
|
3057
3103
|
});
|
|
3058
3104
|
if (isCancel(response)) {
|
|
@@ -3069,8 +3115,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
3069
3115
|
frontend: () => getFrontendChoice(flags.frontend, flags.backend),
|
|
3070
3116
|
backend: ({ results }) => getBackendFrameworkChoice(flags.backend, results.frontend),
|
|
3071
3117
|
runtime: ({ results }) => getRuntimeChoice(flags.runtime, results.backend),
|
|
3072
|
-
database: ({ results }) => getDatabaseChoice(flags.database, results.backend),
|
|
3073
|
-
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),
|
|
3074
3120
|
api: ({ results }) => getApiChoice(flags.api, results.frontend, results.backend),
|
|
3075
3121
|
auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend),
|
|
3076
3122
|
addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend),
|
|
@@ -3148,8 +3194,9 @@ const BackendSchema = z.enum([
|
|
|
3148
3194
|
const RuntimeSchema = z.enum([
|
|
3149
3195
|
"bun",
|
|
3150
3196
|
"node",
|
|
3197
|
+
"workers",
|
|
3151
3198
|
"none"
|
|
3152
|
-
]).describe("Runtime environment");
|
|
3199
|
+
]).describe("Runtime environment (workers only available with hono backend and drizzle orm)");
|
|
3153
3200
|
const FrontendSchema = z.enum([
|
|
3154
3201
|
"tanstack-router",
|
|
3155
3202
|
"react-router",
|
|
@@ -3655,6 +3702,30 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
3655
3702
|
consola$1.fatal("Supabase setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup.");
|
|
3656
3703
|
process.exit(1);
|
|
3657
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
|
+
}
|
|
3658
3729
|
return config;
|
|
3659
3730
|
}
|
|
3660
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
|