Skip to content

Commit 03d9559

Browse files
Simplify auth setup, centralize environment variable management and fix
create readme
1 parent d0540e4 commit 03d9559

File tree

11 files changed

+149
-201
lines changed

11 files changed

+149
-201
lines changed

.changeset/flat-hotels-mate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-better-t-stack": minor
3+
---
4+
5+
Simplify auth setup, centralize environment variable management and fix readme

apps/cli/src/helpers/auth-setup.ts

Lines changed: 16 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
import path from "node:path";
22
import { log } from "@clack/prompts";
3-
import fs from "fs-extra";
43
import pc from "picocolors";
5-
import type { ProjectConfig } from "../types";
64
import { addPackageDependency } from "../utils/add-package-deps";
75

6+
export function generateAuthSecret(length = 32): string {
7+
const characters =
8+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
9+
let result = "";
10+
const charactersLength = characters.length;
11+
for (let i = 0; i < length; i++) {
12+
result += characters.charAt(Math.floor(Math.random() * charactersLength));
13+
}
14+
return result;
15+
}
16+
817
export async function setupAuth(
918
projectDir: string,
1019
enableAuth: boolean,
11-
options: ProjectConfig,
1220
): Promise<void> {
1321
const serverDir = path.join(projectDir, "packages/server");
1422
const clientDir = path.join(projectDir, "packages/client");
@@ -17,90 +25,16 @@ export async function setupAuth(
1725
if (!enableAuth) {
1826
return;
1927
}
20-
2128
addPackageDependency({
2229
dependencies: ["better-auth"],
2330
devDependencies: false,
2431
projectDir: serverDir,
2532
});
26-
27-
const envPath = path.join(serverDir, ".env");
28-
29-
// Create or update the .env file directly with required variables
30-
let envContent = "";
31-
32-
if (await fs.pathExists(envPath)) {
33-
envContent = await fs.readFile(envPath, "utf8");
34-
}
35-
36-
// Only add variables that don't already exist
37-
if (!envContent.includes("BETTER_AUTH_SECRET")) {
38-
envContent += `\nBETTER_AUTH_SECRET=${generateAuthSecret()}`;
39-
}
40-
41-
if (!envContent.includes("BETTER_AUTH_URL")) {
42-
envContent += "\nBETTER_AUTH_URL=http://localhost:3000";
43-
}
44-
45-
if (!envContent.includes("CORS_ORIGIN")) {
46-
envContent += "\nCORS_ORIGIN=http://localhost:3001";
47-
}
48-
49-
if (
50-
options.database === "sqlite" &&
51-
!envContent.includes("TURSO_CONNECTION_URL")
52-
) {
53-
envContent += "\nTURSO_CONNECTION_URL=http://127.0.0.1:8080";
54-
}
55-
56-
if (options.orm === "prisma" && !envContent.includes("DATABASE_URL")) {
57-
if (options.database === "sqlite") {
58-
envContent += '\nDATABASE_URL="file:./dev.db"';
59-
} else if (options.database === "postgres") {
60-
envContent +=
61-
'\nDATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"';
62-
}
63-
}
64-
65-
// Write the updated content
66-
await fs.writeFile(envPath, envContent.trim());
67-
68-
// Create client .env file if it doesn't exist
69-
const clientEnvPath = path.join(clientDir, ".env");
70-
if (!(await fs.pathExists(clientEnvPath))) {
71-
const clientEnvContent = "VITE_SERVER_URL=http://localhost:3000\n";
72-
await fs.writeFile(clientEnvPath, clientEnvContent);
73-
}
74-
75-
if (options.orm === "prisma") {
76-
const packageJsonPath = path.join(projectDir, "package.json");
77-
if (await fs.pathExists(packageJsonPath)) {
78-
const packageJson = await fs.readJson(packageJsonPath);
79-
80-
packageJson.scripts["prisma:generate"] =
81-
"cd packages/server && npx prisma generate";
82-
packageJson.scripts["prisma:push"] =
83-
"cd packages/server && npx prisma db push";
84-
packageJson.scripts["prisma:studio"] =
85-
"cd packages/server && npx prisma studio";
86-
packageJson.scripts["db:setup"] =
87-
"npm run auth:generate && npm run prisma:generate && npm run prisma:push";
88-
89-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
90-
}
91-
} else if (options.orm === "drizzle") {
92-
const packageJsonPath = path.join(projectDir, "package.json");
93-
if (await fs.pathExists(packageJsonPath)) {
94-
const packageJson = await fs.readJson(packageJsonPath);
95-
96-
packageJson.scripts["db:push"] =
97-
"cd packages/server && npx @better-auth/cli migrate";
98-
packageJson.scripts["db:setup"] =
99-
"npm run auth:generate && npm run db:push";
100-
101-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
102-
}
103-
}
33+
addPackageDependency({
34+
dependencies: ["better-auth"],
35+
devDependencies: false,
36+
projectDir: clientDir,
37+
});
10438
} catch (error) {
10539
log.error(pc.red("Failed to configure authentication"));
10640
if (error instanceof Error) {
@@ -109,14 +43,3 @@ export async function setupAuth(
10943
throw error;
11044
}
11145
}
112-
113-
function generateAuthSecret(length = 32): string {
114-
const characters =
115-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
116-
let result = "";
117-
const charactersLength = characters.length;
118-
for (let i = 0; i < length; i++) {
119-
result += characters.charAt(Math.floor(Math.random() * charactersLength));
120-
}
121-
return result;
122-
}

apps/cli/src/helpers/create-project.ts

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { setupAddons } from "./addons-setup";
99
import { setupAuth } from "./auth-setup";
1010
import { createReadme } from "./create-readme";
1111
import { setupDatabase } from "./db-setup";
12+
import { setupEnvironmentVariables } from "./env-setup";
1213
import { displayPostInstallInstructions } from "./post-installation";
1314

1415
export async function createProject(options: ProjectConfig): Promise<string> {
@@ -49,7 +50,9 @@ export async function createProject(options: ProjectConfig): Promise<string> {
4950
options.turso ?? options.database === "sqlite",
5051
);
5152

52-
await setupAuth(projectDir, options.auth, options);
53+
await setupAuth(projectDir, options.auth);
54+
55+
await setupEnvironmentVariables(projectDir, options);
5356

5457
if (options.git) {
5558
await $({ cwd: projectDir })`git init`;
@@ -59,51 +62,63 @@ export async function createProject(options: ProjectConfig): Promise<string> {
5962
await setupAddons(projectDir, options.addons);
6063
}
6164

62-
const packageJsonPath = path.join(projectDir, "package.json");
63-
if (await fs.pathExists(packageJsonPath)) {
64-
const packageJson = await fs.readJson(packageJsonPath);
65+
const rootPackageJsonPath = path.join(projectDir, "package.json");
66+
if (await fs.pathExists(rootPackageJsonPath)) {
67+
const packageJson = await fs.readJson(rootPackageJsonPath);
6568
packageJson.name = options.projectName;
6669

6770
if (options.packageManager !== "bun") {
6871
packageJson.packageManager =
6972
options.packageManager === "npm"
70-
? "npm@10.2.4"
73+
? "npm@10.9.2"
7174
: options.packageManager === "pnpm"
72-
? "pnpm@8.15.4"
75+
? "pnpm@10.6.4"
7376
: options.packageManager === "yarn"
7477
? "yarn@4.1.0"
75-
: "bun@1.2.4";
78+
: "bun@1.2.5";
7679
}
7780

81+
await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 });
82+
}
83+
84+
const serverPackageJsonPath = path.join(
85+
projectDir,
86+
"packages/server/package.json",
87+
);
88+
if (await fs.pathExists(serverPackageJsonPath)) {
89+
const serverPackageJson = await fs.readJson(serverPackageJsonPath);
90+
7891
if (options.database !== "none") {
79-
if (options.database === "sqlite") {
80-
packageJson.scripts["db:local"] =
81-
"cd packages/server && turso dev --db-file local.db";
92+
if (options.database === "sqlite" && options.turso) {
93+
serverPackageJson.scripts["db:local"] =
94+
"turso dev --db-file local.db";
8295
}
8396

8497
if (options.auth) {
85-
packageJson.scripts["auth:generate"] =
86-
"cd packages/server && npx @better-auth/cli generate --output ./src/db/auth-schema.ts";
98+
serverPackageJson.scripts["auth:generate"] =
99+
"npx @better-auth/cli generate --output ./src/db/auth-schema.ts";
87100

88101
if (options.orm === "prisma") {
89-
packageJson.scripts["prisma:generate"] =
90-
"cd packages/server && npx prisma generate";
91-
packageJson.scripts["prisma:push"] =
92-
"cd packages/server && npx prisma db push";
93-
packageJson.scripts["prisma:studio"] =
94-
"cd packages/server && npx prisma studio";
95-
packageJson.scripts["db:setup"] =
96-
"npm run auth:generate && npm run prisma:generate && npm run prisma:push";
102+
serverPackageJson.scripts["db:push"] = "npx prisma db push";
103+
serverPackageJson.scripts["db:studio"] = "npx prisma studio";
104+
} else if (options.orm === "drizzle") {
105+
serverPackageJson.scripts["db:push"] = "npx drizzle-kit push";
106+
serverPackageJson.scripts["db:studio"] = "npx drizzle-kit studio";
107+
}
108+
} else {
109+
if (options.orm === "prisma") {
110+
serverPackageJson.scripts["db:push"] = "npx prisma db push";
111+
serverPackageJson.scripts["db:studio"] = "npx prisma studio";
97112
} else if (options.orm === "drizzle") {
98-
packageJson.scripts["drizzle:migrate"] =
99-
"cd packages/server && npx @better-auth/cli migrate";
100-
packageJson.scripts["db:setup"] =
101-
"npm run auth:generate && npm run drizzle:migrate";
113+
serverPackageJson.scripts["db:push"] = "npx drizzle-kit push";
114+
serverPackageJson.scripts["db:studio"] = "npx drizzle-kit studio";
102115
}
103116
}
104117
}
105118

106-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
119+
await fs.writeJson(serverPackageJsonPath, serverPackageJson, {
120+
spaces: 2,
121+
});
107122
}
108123

109124
await createReadme(projectDir, options);

apps/cli/src/helpers/create-readme.ts

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ function generateDatabaseSetup(
126126
127127
1. Start the local SQLite database:
128128
\`\`\`bash
129-
${packageManagerRunCmd} db:local
129+
cd packages/server && ${packageManagerRunCmd} db:local
130130
\`\`\`
131131
132-
2. Update your \`.env\` file with the appropriate connection details if needed.
132+
2. Update your \`.env\` file in the \`packages/server\` directory with the appropriate connection details if needed.
133133
`;
134134
} else if (database === "postgres") {
135135
setup += `This project uses PostgreSQL${orm === "drizzle" ? " with Drizzle ORM" : " with Prisma"}.
@@ -143,23 +143,24 @@ ${packageManagerRunCmd} db:local
143143
setup += `
144144
3. Generate the authentication schema:
145145
\`\`\`bash
146-
${packageManagerRunCmd} auth:generate
146+
cd packages/server && ${packageManagerRunCmd} auth:generate
147147
\`\`\`
148+
`;
149+
}
148150

149-
4. ${
150-
orm === "prisma"
151-
? `Generate the Prisma client and push the schema:
151+
setup += `
152+
${auth ? "4" : "3"}. ${
153+
orm === "prisma"
154+
? `Generate the Prisma client and push the schema:
152155
\`\`\`bash
153-
${packageManagerRunCmd} prisma:generate
154156
${packageManagerRunCmd} db:push
155157
\`\`\``
156-
: `Apply the Drizzle migrations:
158+
: `Apply the schema to your database:
157159
\`\`\`bash
158160
${packageManagerRunCmd} db:push
159161
\`\`\``
160-
}
161-
`;
162162
}
163+
`;
163164

164165
return setup;
165166
}
@@ -173,32 +174,21 @@ function generateScriptsList(
173174
let scripts = `- \`${packageManagerRunCmd} dev\`: Start both client and server in development mode
174175
- \`${packageManagerRunCmd} build\`: Build both client and server
175176
- \`${packageManagerRunCmd} dev:client\`: Start only the client
176-
- \`${packageManagerRunCmd} dev:server\`: Start only the server`;
177+
- \`${packageManagerRunCmd} dev:server\`: Start only the server
178+
- \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all packages`;
177179

178180
if (database !== "none") {
179-
if (database === "sqlite") {
180-
scripts += `\n- \`${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
181-
}
182-
183-
if (orm === "prisma") {
184-
scripts += `
185-
- \`${packageManagerRunCmd} prisma:generate\`: Generate Prisma client
186-
- \`${packageManagerRunCmd} db:push\`: Push schema changes to database
187-
- \`${packageManagerRunCmd} prisma:studio\`: Open Prisma Studio`;
188-
} else if (orm === "drizzle") {
189-
scripts += `
190-
- \`${packageManagerRunCmd} db:generate\`: Generate database schema
181+
scripts += `
191182
- \`${packageManagerRunCmd} db:push\`: Push schema changes to database
192-
- \`${packageManagerRunCmd} db:studio\`: Open Drizzle Studio`;
183+
- \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
184+
185+
if (database === "sqlite" && orm === "drizzle") {
186+
scripts += `\n- \`cd packages/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
193187
}
194188
}
195189

196190
if (auth) {
197-
scripts += `\n- \`${packageManagerRunCmd} auth:generate\`: Generate authentication schema`;
198-
}
199-
200-
if (auth && database !== "none") {
201-
scripts += `\n- \`${packageManagerRunCmd} db:setup\`: Complete database setup for auth`;
191+
scripts += `\n- \`cd packages/server && ${packageManagerRunCmd} auth:generate\`: Generate authentication schema`;
202192
}
203193

204194
return scripts;

apps/cli/src/helpers/db-setup.ts

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -74,43 +74,6 @@ export async function setupDatabase(
7474
});
7575
}
7676
}
77-
78-
const packageJsonPath = path.join(serverDir, "package.json");
79-
if (await fs.pathExists(packageJsonPath)) {
80-
const packageJson = await fs.readJSON(packageJsonPath);
81-
82-
if (orm === "drizzle") {
83-
packageJson.scripts = {
84-
...packageJson.scripts,
85-
"db:generate": "drizzle-kit generate",
86-
"db:migrate": "drizzle-kit push",
87-
"db:studio": "drizzle-kit studio",
88-
};
89-
} else if (orm === "prisma") {
90-
packageJson.scripts = {
91-
...packageJson.scripts,
92-
"prisma:generate": "prisma generate",
93-
"prisma:push": "prisma db push",
94-
"prisma:studio": "prisma studio",
95-
};
96-
}
97-
98-
await fs.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
99-
}
100-
101-
if (orm === "prisma") {
102-
const envPath = path.join(serverDir, ".env");
103-
if (await fs.pathExists(envPath)) {
104-
const envContent = await fs.readFile(envPath, "utf8");
105-
if (!envContent.includes("DATABASE_URL")) {
106-
const databaseUrlLine =
107-
databaseType === "sqlite"
108-
? `\nDATABASE_URL="file:./dev.db"`
109-
: `\nDATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"`;
110-
await fs.appendFile(envPath, databaseUrlLine);
111-
}
112-
}
113-
}
11477
} catch (error) {
11578
s.stop(pc.red("Failed to set up database"));
11679
if (error instanceof Error) {

0 commit comments

Comments
 (0)