Skip to content

Commit ac2f220

Browse files
simplify auth setup
1 parent 8678ec6 commit ac2f220

File tree

26 files changed

+309
-222
lines changed

26 files changed

+309
-222
lines changed

.changeset/busy-foxes-love.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": patch
3+
---
4+
5+
simplify auth setup

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

Lines changed: 73 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import pc from "picocolors";
55
import { PKG_ROOT } from "../constants";
66
import type { ProjectConfig } from "../types";
77

8-
export async function configureAuth(
8+
export async function setupAuth(
99
projectDir: string,
1010
enableAuth: boolean,
1111
hasDatabase: boolean,
@@ -16,177 +16,98 @@ export async function configureAuth(
1616

1717
try {
1818
if (!enableAuth) {
19-
await fs.remove(path.join(clientDir, "src/components/sign-up-form.tsx"));
20-
await fs.remove(path.join(clientDir, "src/components/sign-in-form.tsx"));
21-
await fs.remove(path.join(clientDir, "src/components/auth-forms.tsx"));
22-
await fs.remove(path.join(clientDir, "src/components/user-menu.tsx"));
23-
await fs.remove(path.join(clientDir, "src/lib/auth-client.ts"));
24-
25-
const indexRoutePath = path.join(clientDir, "src/routes/index.tsx");
26-
const indexRouteContent = await fs.readFile(indexRoutePath, "utf8");
27-
const updatedIndexRouteContent = indexRouteContent
28-
.replace(/import AuthForms from "@\/components\/auth-forms";\n/, "")
29-
.replace(/<AuthForms \/>/, "");
30-
await fs.writeFile(indexRoutePath, updatedIndexRouteContent, "utf8");
31-
32-
await fs.remove(path.join(serverDir, "src/lib/auth.ts"));
33-
34-
const indexFilePath = path.join(serverDir, "src/index.ts");
35-
const indexContent = await fs.readFile(indexFilePath, "utf8");
36-
const updatedIndexContent = indexContent
37-
.replace(/import { auth } from "\.\/lib\/auth";\n/, "")
38-
.replace(
39-
/app\.on\(\["POST", "GET"\], "\/api\/auth\/\*\*", \(c\) => auth\.handler\(c\.req\.raw\)\);\n\n/,
40-
"",
41-
);
42-
await fs.writeFile(indexFilePath, updatedIndexContent, "utf8");
43-
44-
const contextFilePath = path.join(serverDir, "src/lib/context.ts");
45-
const contextContent = await fs.readFile(contextFilePath, "utf8");
46-
const updatedContextContent = contextContent
47-
.replace(/import { auth } from "\.\/auth";\n/, "")
48-
.replace(
49-
/const session = await auth\.api\.getSession\({\n\s+headers: hono\.req\.raw\.headers,\n\s+}\);/,
50-
"const session = null;",
51-
);
52-
await fs.writeFile(contextFilePath, updatedContextContent, "utf8");
53-
} else if (!hasDatabase) {
19+
return;
20+
}
21+
22+
if (!hasDatabase) {
5423
log.warn(
5524
pc.yellow(
5625
"Authentication enabled but no database selected. Auth will not function properly.",
5726
),
5827
);
59-
} else {
60-
const envPath = path.join(serverDir, ".env");
61-
const templateEnvPath = path.join(
62-
PKG_ROOT,
63-
getOrmTemplatePath(
64-
options.orm,
65-
options.database,
66-
"packages/server/_env",
67-
),
68-
);
28+
return;
29+
}
6930

70-
if (!(await fs.pathExists(envPath))) {
71-
if (await fs.pathExists(templateEnvPath)) {
72-
await fs.copy(templateEnvPath, envPath);
73-
} else {
74-
const defaultEnv = `BETTER_AUTH_SECRET=${generateAuthSecret()}
31+
const envPath = path.join(serverDir, ".env");
32+
const templateEnvPath = path.join(
33+
PKG_ROOT,
34+
getOrmTemplatePath(options.orm, options.database, "packages/server/_env"),
35+
);
36+
37+
if (!(await fs.pathExists(envPath))) {
38+
if (await fs.pathExists(templateEnvPath)) {
39+
await fs.copy(templateEnvPath, envPath);
40+
} else {
41+
const defaultEnv = `BETTER_AUTH_SECRET=${generateAuthSecret()}
7542
BETTER_AUTH_URL=http://localhost:3000
7643
CORS_ORIGIN=http://localhost:3001
7744
${options.database === "sqlite" ? "TURSO_CONNECTION_URL=http://127.0.0.1:8080" : ""}
7845
${options.orm === "prisma" ? 'DATABASE_URL="file:./dev.db"' : ""}
7946
`;
80-
await fs.writeFile(envPath, defaultEnv);
81-
}
82-
} else {
83-
let envContent = await fs.readFile(envPath, "utf8");
84-
85-
if (!envContent.includes("BETTER_AUTH_SECRET")) {
86-
envContent += `\nBETTER_AUTH_SECRET=${generateAuthSecret()}`;
87-
}
47+
await fs.writeFile(envPath, defaultEnv);
48+
}
49+
} else {
50+
let envContent = await fs.readFile(envPath, "utf8");
8851

89-
if (!envContent.includes("BETTER_AUTH_URL")) {
90-
envContent += "\nBETTER_AUTH_URL=http://localhost:3000";
91-
}
52+
if (!envContent.includes("BETTER_AUTH_SECRET")) {
53+
envContent += `\nBETTER_AUTH_SECRET=${generateAuthSecret()}`;
54+
}
9255

93-
if (!envContent.includes("CORS_ORIGIN")) {
94-
envContent += "\nCORS_ORIGIN=http://localhost:3001";
95-
}
56+
if (!envContent.includes("BETTER_AUTH_URL")) {
57+
envContent += "\nBETTER_AUTH_URL=http://localhost:3000";
58+
}
9659

97-
if (
98-
options.database === "sqlite" &&
99-
!envContent.includes("TURSO_CONNECTION_URL")
100-
) {
101-
envContent += "\nTURSO_CONNECTION_URL=http://127.0.0.1:8080";
102-
}
60+
if (!envContent.includes("CORS_ORIGIN")) {
61+
envContent += "\nCORS_ORIGIN=http://localhost:3001";
62+
}
10363

104-
if (options.orm === "prisma" && !envContent.includes("DATABASE_URL")) {
105-
envContent += '\nDATABASE_URL="file:./dev.db"';
106-
}
64+
if (
65+
options.database === "sqlite" &&
66+
!envContent.includes("TURSO_CONNECTION_URL")
67+
) {
68+
envContent += "\nTURSO_CONNECTION_URL=http://127.0.0.1:8080";
69+
}
10770

108-
await fs.writeFile(envPath, envContent);
71+
if (options.orm === "prisma" && !envContent.includes("DATABASE_URL")) {
72+
envContent += '\nDATABASE_URL="file:./dev.db"';
10973
}
11074

111-
const clientEnvPath = path.join(clientDir, ".env");
112-
if (!(await fs.pathExists(clientEnvPath))) {
113-
const clientEnvContent = "VITE_SERVER_URL=http://localhost:3000\n";
114-
await fs.writeFile(clientEnvPath, clientEnvContent);
75+
await fs.writeFile(envPath, envContent);
76+
}
77+
78+
const clientEnvPath = path.join(clientDir, ".env");
79+
if (!(await fs.pathExists(clientEnvPath))) {
80+
const clientEnvContent = "VITE_SERVER_URL=http://localhost:3000\n";
81+
await fs.writeFile(clientEnvPath, clientEnvContent);
82+
}
83+
84+
if (options.orm === "prisma") {
85+
const packageJsonPath = path.join(projectDir, "package.json");
86+
if (await fs.pathExists(packageJsonPath)) {
87+
const packageJson = await fs.readJson(packageJsonPath);
88+
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";
97+
98+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
11599
}
100+
} else if (options.orm === "drizzle") {
101+
const packageJsonPath = path.join(projectDir, "package.json");
102+
if (await fs.pathExists(packageJsonPath)) {
103+
const packageJson = await fs.readJson(packageJsonPath);
104+
105+
packageJson.scripts["db:push"] =
106+
"cd packages/server && npx @better-auth/cli migrate";
107+
packageJson.scripts["db:setup"] =
108+
"npm run auth:generate && npm run db:push";
116109

117-
if (options.orm === "prisma") {
118-
const prismaAuthPath = path.join(serverDir, "src/lib/auth.ts");
119-
const defaultPrismaAuthPath = path.join(
120-
PKG_ROOT,
121-
getOrmTemplatePath(
122-
options.orm,
123-
options.database,
124-
"packages/server/src/lib/auth.ts",
125-
),
126-
);
127-
128-
if (
129-
(await fs.pathExists(defaultPrismaAuthPath)) &&
130-
!(await fs.pathExists(prismaAuthPath))
131-
) {
132-
await fs.ensureDir(path.dirname(prismaAuthPath));
133-
await fs.copy(defaultPrismaAuthPath, prismaAuthPath);
134-
}
135-
136-
let authContent = await fs.readFile(prismaAuthPath, "utf8");
137-
if (!authContent.includes("trustedOrigins")) {
138-
authContent = authContent.replace(
139-
"export const auth = betterAuth({",
140-
"export const auth = betterAuth({\n trustedOrigins: [process.env.CORS_ORIGIN!],",
141-
);
142-
await fs.writeFile(prismaAuthPath, authContent);
143-
}
144-
145-
const packageJsonPath = path.join(projectDir, "package.json");
146-
if (await fs.pathExists(packageJsonPath)) {
147-
const packageJson = await fs.readJson(packageJsonPath);
148-
149-
packageJson.scripts["prisma:generate"] =
150-
"cd packages/server && npx prisma generate";
151-
packageJson.scripts["prisma:push"] =
152-
"cd packages/server && npx prisma db push";
153-
packageJson.scripts["prisma:studio"] =
154-
"cd packages/server && npx prisma studio";
155-
packageJson.scripts["db:setup"] =
156-
"npm run auth:generate && npm run prisma:generate && npm run prisma:push";
157-
158-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
159-
}
160-
} else if (options.orm === "drizzle") {
161-
const drizzleAuthPath = path.join(serverDir, "src/lib/auth.ts");
162-
const defaultDrizzleAuthPath = path.join(
163-
PKG_ROOT,
164-
getOrmTemplatePath(
165-
options.orm,
166-
options.database,
167-
"packages/server/src/lib/auth.ts",
168-
),
169-
);
170-
171-
if (
172-
(await fs.pathExists(defaultDrizzleAuthPath)) &&
173-
!(await fs.pathExists(drizzleAuthPath))
174-
) {
175-
await fs.ensureDir(path.dirname(drizzleAuthPath));
176-
await fs.copy(defaultDrizzleAuthPath, drizzleAuthPath);
177-
}
178-
179-
const packageJsonPath = path.join(projectDir, "package.json");
180-
if (await fs.pathExists(packageJsonPath)) {
181-
const packageJson = await fs.readJson(packageJsonPath);
182-
183-
packageJson.scripts["db:push"] =
184-
"cd packages/server && npx @better-auth/cli migrate";
185-
packageJson.scripts["db:setup"] =
186-
"npm run auth:generate && npm run db:push";
187-
188-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
189-
}
110+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
190111
}
191112
}
192113
} catch (error) {

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import pc from "picocolors";
66
import { PKG_ROOT } from "../constants";
77
import type { ProjectConfig } from "../types";
88
import { setupAddons } from "./addons-setup";
9-
import { configureAuth } from "./auth-setup";
9+
import { setupAuth } from "./auth-setup";
1010
import { createReadme } from "./create-readme";
1111
import { setupDatabase } from "./db-setup";
1212
import { displayPostInstallInstructions } from "./post-installation";
@@ -24,6 +24,13 @@ export async function createProject(options: ProjectConfig): Promise<string> {
2424
}
2525
await fs.copy(templateDir, projectDir);
2626

27+
if (options.auth) {
28+
const authTemplateDir = path.join(PKG_ROOT, "template/with-auth");
29+
if (await fs.pathExists(authTemplateDir)) {
30+
await fs.copy(authTemplateDir, projectDir, { overwrite: true });
31+
}
32+
}
33+
2734
if (options.orm !== "none" && options.database !== "none") {
2835
const ormTemplateDir = path.join(
2936
PKG_ROOT,
@@ -40,6 +47,10 @@ export async function createProject(options: ProjectConfig): Promise<string> {
4047
path.join(projectDir, "packages/server/_env"),
4148
path.join(projectDir, "packages/server/.env"),
4249
],
50+
[
51+
path.join(projectDir, "packages/client/_env"),
52+
path.join(projectDir, "packages/client/.env"),
53+
],
4354
];
4455

4556
for (const [source, target] of envFiles) {
@@ -58,7 +69,8 @@ export async function createProject(options: ProjectConfig): Promise<string> {
5869
options.orm,
5970
options.turso ?? options.database === "sqlite",
6071
);
61-
await configureAuth(
72+
73+
await setupAuth(
6274
projectDir,
6375
options.auth,
6476
options.database !== "none",

apps/cli/template/base/packages/client/src/components/header.tsx

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Link } from "@tanstack/react-router";
22
import { ModeToggle } from "./mode-toggle";
3-
import UserMenu from "./user-menu";
43

54
export default function Header() {
65
return (
@@ -16,27 +15,9 @@ export default function Header() {
1615
>
1716
Home
1817
</Link>
19-
<Link
20-
to="/dashboard"
21-
activeProps={{
22-
className: "font-bold",
23-
}}
24-
activeOptions={{ exact: true }}
25-
>
26-
Dashboard
27-
</Link>
28-
<Link
29-
to="/about"
30-
activeProps={{
31-
className: "font-bold",
32-
}}
33-
>
34-
About
35-
</Link>
3618
</div>
3719
<div className="flex flex-row items-center gap-2">
3820
<ModeToggle />
39-
<UserMenu />
4021
</div>
4122
</div>
4223
<hr />

apps/cli/template/base/packages/client/src/routes/about.tsx

Lines changed: 0 additions & 13 deletions
This file was deleted.

apps/cli/template/base/packages/client/src/routes/index.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import AuthForms from "@/components/auth-forms";
21
import { trpc } from "@/utils/trpc";
32
import { createFileRoute, Link } from "@tanstack/react-router";
43

@@ -13,7 +12,6 @@ function HomeComponent() {
1312
<h3>Welcome Home!</h3>
1413
<Link to="/dashboard">Go to Dashboard</Link>
1514
<p>healthCheck: {healthCheck.data}</p>
16-
<AuthForms />
1715
</div>
1816
);
1917
}

apps/cli/template/base/packages/server/src/index.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import "dotenv/config";
44
import { Hono } from "hono";
55
import { cors } from "hono/cors";
66
import { logger } from "hono/logger";
7-
import { auth } from "./lib/auth";
87
import { createContext } from "./lib/context";
98
import { appRouter } from "./routers/index";
109

@@ -17,13 +16,9 @@ app.use(
1716
cors({
1817
origin: process.env.CORS_ORIGIN!,
1918
allowMethods: ["GET", "POST", "OPTIONS"],
20-
allowHeaders: ["Content-Type", "Authorization"],
21-
credentials: true,
2219
}),
2320
);
2421

25-
app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
26-
2722
app.use(
2823
"/trpc/*",
2924
trpcServer({

0 commit comments

Comments
 (0)