stackkit 0.2.6 → 0.2.8
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/README.md +87 -12
- package/dist/cli/create.js +1 -2
- package/dist/cli/list.js +73 -83
- package/dist/index.js +25 -44
- package/dist/lib/constants.d.ts +110 -0
- package/dist/lib/constants.js +112 -0
- package/dist/lib/env/env-editor.js +41 -47
- package/dist/lib/fs/files.d.ts +0 -1
- package/dist/lib/fs/files.js +12 -40
- package/dist/lib/generation/code-generator.d.ts +4 -1
- package/dist/lib/generation/code-generator.js +36 -10
- package/dist/lib/pm/package-manager.d.ts +1 -1
- package/dist/lib/pm/package-manager.js +130 -14
- package/dist/lib/ui/logger.d.ts +8 -1
- package/dist/lib/ui/logger.js +60 -3
- package/dist/lib/utils/fs-helpers.d.ts +12 -0
- package/dist/lib/utils/fs-helpers.js +61 -0
- package/dist/lib/utils/json-loader.d.ts +6 -0
- package/dist/lib/utils/json-loader.js +34 -0
- package/dist/lib/utils/module-loader.d.ts +9 -0
- package/dist/lib/utils/module-loader.js +98 -0
- package/dist/lib/utils/package-root.d.ts +1 -0
- package/dist/lib/utils/package-root.js +75 -2
- package/dist/lib/utils/path-resolver.d.ts +9 -0
- package/dist/lib/utils/path-resolver.js +44 -0
- package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +3 -0
- package/modules/auth/authjs/files/nextjs/proxy.ts +1 -0
- package/modules/auth/authjs/files/shared/lib/auth.ts +119 -0
- package/modules/auth/authjs/files/{prisma → shared/prisma}/schema.prisma +11 -1
- package/modules/auth/authjs/generator.json +18 -8
- package/modules/auth/better-auth/files/express/middlewares/authorize.ts +54 -0
- package/modules/auth/better-auth/files/express/types/express.d.ts +16 -0
- package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +31 -0
- package/modules/auth/better-auth/files/nextjs/proxy.ts +34 -0
- package/modules/auth/better-auth/files/{lib → shared/lib}/auth-client.ts +1 -1
- package/modules/auth/better-auth/files/{lib → shared/lib}/auth.ts +46 -20
- package/modules/auth/better-auth/files/{prisma → shared/prisma}/schema.prisma +11 -2
- package/modules/auth/better-auth/generator.json +74 -19
- package/modules/database/mongoose/generator.json +16 -2
- package/modules/database/prisma/files/lib/prisma.ts +1 -1
- package/modules/database/prisma/files/prisma/schema.prisma +1 -2
- package/modules/database/prisma/generator.json +8 -1
- package/package.json +2 -2
- package/templates/express/env.example +2 -1
- package/templates/express/package.json +3 -4
- package/templates/express/src/app.ts +18 -25
- package/templates/express/src/config/cors.ts +12 -0
- package/templates/express/src/config/helmet.ts +5 -0
- package/templates/express/src/config/logger.ts +6 -0
- package/templates/express/src/config/rate-limit.ts +11 -0
- package/templates/express/src/{features → modules}/health/health.route.ts +1 -1
- package/templates/express/src/routes/index.ts +12 -0
- package/templates/express/src/shared/errors/api-error.ts +14 -0
- package/templates/express/src/shared/errors/error-codes.ts +9 -0
- package/templates/express/src/shared/logger/logger.ts +20 -0
- package/templates/express/src/{middlewares → shared/middlewares}/error.middleware.ts +1 -1
- package/templates/express/src/shared/middlewares/not-found.middleware.ts +9 -0
- package/templates/express/src/shared/utils/async-handler.ts +9 -0
- package/templates/express/src/shared/utils/pagination.ts +6 -0
- package/templates/express/src/shared/utils/response.ts +9 -0
- package/templates/express/tsconfig.json +9 -3
- package/templates/react/src/app/layouts/dashboard-layout.tsx +8 -0
- package/templates/react/src/app/layouts/public-layout.tsx +5 -0
- package/templates/react/src/app/providers.tsx +20 -0
- package/templates/react/src/app/router.tsx +21 -0
- package/templates/react/src/{pages/About.tsx → features/about/pages/about.tsx} +1 -1
- package/templates/react/src/{pages/Home.tsx → features/home/pages/home.tsx} +1 -1
- package/templates/react/src/main.tsx +2 -2
- package/templates/react/src/{api/client.ts → shared/api/http.ts} +1 -1
- package/templates/react/src/{pages/NotFound.tsx → shared/pages/not-found.tsx} +1 -1
- package/dist/lib/git-utils.d.ts +0 -1
- package/dist/lib/git-utils.js +0 -29
- package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +0 -2
- package/modules/auth/authjs/files/lib/auth.ts +0 -22
- /package/modules/auth/better-auth/files/{api → nextjs/api}/auth/[...all]/route.ts +0 -0
- /package/modules/auth/better-auth/files/{lib → shared/lib/email}/email-service.ts +0 -0
- /package/modules/auth/better-auth/files/{lib → shared/lib/email}/email-templates.ts +0 -0
- /package/templates/express/src/{features → modules}/health/health.controller.ts +0 -0
- /package/templates/express/src/{features → modules}/health/health.service.ts +0 -0
- /package/templates/react/src/{components/ErrorBoundary.tsx → shared/components/error-boundary.tsx} +0 -0
- /package/templates/react/src/{components/Layout.tsx → shared/components/layout.tsx} +0 -0
- /package/templates/react/src/{components/Loading.tsx → shared/components/loading.tsx} +0 -0
- /package/templates/react/src/{components/SEO.tsx → shared/components/seo.tsx} +0 -0
- /package/templates/react/src/{lib/queryClient.ts → shared/lib/query-client.ts} +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getModulesPath = getModulesPath;
|
|
7
|
+
exports.getTemplatesPath = getTemplatesPath;
|
|
8
|
+
exports.getModulePath = getModulePath;
|
|
9
|
+
exports.getModuleJsonPath = getModuleJsonPath;
|
|
10
|
+
exports.getGeneratorJsonPath = getGeneratorJsonPath;
|
|
11
|
+
exports.getModuleFilesPath = getModuleFilesPath;
|
|
12
|
+
exports.getTemplateJsonPath = getTemplateJsonPath;
|
|
13
|
+
exports.getDatabaseModulesPath = getDatabaseModulesPath;
|
|
14
|
+
exports.getAuthModulesPath = getAuthModulesPath;
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const constants_1 = require("../constants");
|
|
17
|
+
const package_root_1 = require("./package-root");
|
|
18
|
+
function getModulesPath() {
|
|
19
|
+
return path_1.default.join((0, package_root_1.getPackageRoot)(), constants_1.DIRECTORY_NAMES.MODULES);
|
|
20
|
+
}
|
|
21
|
+
function getTemplatesPath() {
|
|
22
|
+
return path_1.default.join((0, package_root_1.getPackageRoot)(), constants_1.DIRECTORY_NAMES.TEMPLATES);
|
|
23
|
+
}
|
|
24
|
+
function getModulePath(category, moduleName) {
|
|
25
|
+
return path_1.default.join(getModulesPath(), category, moduleName);
|
|
26
|
+
}
|
|
27
|
+
function getModuleJsonPath(category, moduleName) {
|
|
28
|
+
return path_1.default.join(getModulePath(category, moduleName), constants_1.FILE_NAMES.MODULE_JSON);
|
|
29
|
+
}
|
|
30
|
+
function getGeneratorJsonPath(category, moduleName) {
|
|
31
|
+
return path_1.default.join(getModulePath(category, moduleName), constants_1.FILE_NAMES.GENERATOR_JSON);
|
|
32
|
+
}
|
|
33
|
+
function getModuleFilesPath(category, moduleName) {
|
|
34
|
+
return path_1.default.join(getModulePath(category, moduleName), constants_1.DIRECTORY_NAMES.FILES);
|
|
35
|
+
}
|
|
36
|
+
function getTemplateJsonPath(frameworkName) {
|
|
37
|
+
return path_1.default.join(getTemplatesPath(), frameworkName, constants_1.FILE_NAMES.TEMPLATE_JSON);
|
|
38
|
+
}
|
|
39
|
+
function getDatabaseModulesPath() {
|
|
40
|
+
return path_1.default.join(getModulesPath(), constants_1.MODULE_CATEGORIES.DATABASE);
|
|
41
|
+
}
|
|
42
|
+
function getAuthModulesPath() {
|
|
43
|
+
return path_1.default.join(getModulesPath(), constants_1.MODULE_CATEGORIES.AUTH);
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { auth as proxy } from "@/lib/auth";
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { PrismaAdapter } from "@auth/prisma-adapter";
|
|
3
|
+
import bcrypt from "bcryptjs";
|
|
4
|
+
import NextAuth from "next-auth";
|
|
5
|
+
import Credentials from "next-auth/providers/credentials";
|
|
6
|
+
import EmailProvider from "next-auth/providers/email";
|
|
7
|
+
import Google from "next-auth/providers/google";
|
|
8
|
+
import {
|
|
9
|
+
getUserById,
|
|
10
|
+
getUserByProviderAccountId,
|
|
11
|
+
getUserByUsername,
|
|
12
|
+
} from "./actions/auth";
|
|
13
|
+
import { sendEmail } from "./email/email-service";
|
|
14
|
+
import { getVerificationEmailTemplate } from "./email/email-templates";
|
|
15
|
+
import { prisma } from "./prisma";
|
|
16
|
+
|
|
17
|
+
const options: any = {
|
|
18
|
+
adapter: PrismaAdapter(prisma),
|
|
19
|
+
providers: [
|
|
20
|
+
Google({
|
|
21
|
+
clientId: process.env.GOOGLE_CLIENT_ID ?? "",
|
|
22
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
|
|
23
|
+
profile(profile: any) {
|
|
24
|
+
const p = profile as Record<string, any>;
|
|
25
|
+
return {
|
|
26
|
+
id: p.sub ?? p.id,
|
|
27
|
+
name: p.name,
|
|
28
|
+
email: p.email,
|
|
29
|
+
image: p.picture,
|
|
30
|
+
role: p.role ?? "USER",
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
(Credentials({
|
|
35
|
+
id: "credentials",
|
|
36
|
+
name: "Credentials",
|
|
37
|
+
credentials: {
|
|
38
|
+
username: { label: "Email", type: "text" },
|
|
39
|
+
password: { label: "Password", type: "password" },
|
|
40
|
+
},
|
|
41
|
+
async authorize(credentials: any) {
|
|
42
|
+
if (!credentials?.username || !credentials?.password) return null;
|
|
43
|
+
const user = await getUserByUsername(credentials.username as string);
|
|
44
|
+
if (!user) return null;
|
|
45
|
+
const hashed = (user as any).password;
|
|
46
|
+
if (!hashed) return null; // OAuth-only account
|
|
47
|
+
const ok = await bcrypt.compare(credentials.password as string, hashed);
|
|
48
|
+
if (!ok) return null;
|
|
49
|
+
// strip sensitive fields
|
|
50
|
+
const safe = { ...user } as Record<string, any>;
|
|
51
|
+
delete safe.password;
|
|
52
|
+
return safe;
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
EmailProvider({
|
|
56
|
+
server: process.env.EMAIL_SERVER,
|
|
57
|
+
from: process.env.EMAIL_FROM,
|
|
58
|
+
async sendVerificationRequest({ identifier, url, provider }: any) {
|
|
59
|
+
const { html, text } = getVerificationEmailTemplate(
|
|
60
|
+
{ email: identifier },
|
|
61
|
+
url,
|
|
62
|
+
);
|
|
63
|
+
await sendEmail({
|
|
64
|
+
to: identifier,
|
|
65
|
+
subject: "Verify your email",
|
|
66
|
+
html,
|
|
67
|
+
text,
|
|
68
|
+
from: provider?.from,
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
})),
|
|
72
|
+
],
|
|
73
|
+
callbacks: {
|
|
74
|
+
// ensure provider-account linking is safe
|
|
75
|
+
async signIn({ user, account }: any) {
|
|
76
|
+
if (account && account.provider !== "credentials") {
|
|
77
|
+
const linked = await getUserByProviderAccountId(
|
|
78
|
+
account.providerAccountId as string,
|
|
79
|
+
);
|
|
80
|
+
if (linked && linked.id !== user.id)
|
|
81
|
+
return "/sign-in/?error=OAuthAccountNotLinked";
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
},
|
|
85
|
+
// keep session payload minimal
|
|
86
|
+
async jwt({ token, user }: any) {
|
|
87
|
+
if (user) {
|
|
88
|
+
token.user = {
|
|
89
|
+
id: user.id,
|
|
90
|
+
email: user.email,
|
|
91
|
+
name: user.name,
|
|
92
|
+
role: user.role ?? "USER",
|
|
93
|
+
};
|
|
94
|
+
token.role = token.user.role;
|
|
95
|
+
}
|
|
96
|
+
if (token.sub && !token.user) {
|
|
97
|
+
const dbUser = await getUserById(token.sub as string);
|
|
98
|
+
if (dbUser)
|
|
99
|
+
token.user = {
|
|
100
|
+
id: dbUser.id,
|
|
101
|
+
email: dbUser.email,
|
|
102
|
+
name: dbUser.name,
|
|
103
|
+
role: (dbUser as any).role ?? "USER",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return token;
|
|
107
|
+
},
|
|
108
|
+
async session({ session, token }: any) {
|
|
109
|
+
session.user = token.user;
|
|
110
|
+
session.role = token.role ?? "USER";
|
|
111
|
+
return session;
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
session: { strategy: "jwt" },
|
|
115
|
+
secret: process.env.AUTH_SECRET,
|
|
116
|
+
debug: process.env.NODE_ENV !== "production",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const { handlers, signIn, signOut, auth } = NextAuth(options);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
{{#
|
|
1
|
+
{{#if prismaProvider == "mongodb"}}
|
|
2
|
+
{{#var defaultId = @default(auto()) @map("_id") @db.ObjectId}}
|
|
3
|
+
{{else}}
|
|
4
|
+
{{#var defaultId = @default(cuid())}}
|
|
5
|
+
{{/if}}
|
|
2
6
|
model Account {
|
|
3
7
|
id String @id {{defaultId}}
|
|
4
8
|
userId String @map("user_id")
|
|
@@ -35,6 +39,7 @@ model User {
|
|
|
35
39
|
email String? @unique
|
|
36
40
|
emailVerified DateTime? @map("email_verified")
|
|
37
41
|
image String?
|
|
42
|
+
role Role @default(USER)
|
|
38
43
|
accounts Account[]
|
|
39
44
|
sessions Session[]
|
|
40
45
|
|
|
@@ -48,4 +53,9 @@ model VerificationToken {
|
|
|
48
53
|
|
|
49
54
|
@@unique([identifier, token])
|
|
50
55
|
@@map("verification_tokens")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
enum Role {
|
|
59
|
+
USER
|
|
60
|
+
ADMIN
|
|
51
61
|
}
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
"operations": [
|
|
6
6
|
{
|
|
7
7
|
"type": "create-file",
|
|
8
|
-
"source": "lib/auth.ts",
|
|
9
|
-
"destination": "
|
|
8
|
+
"source": "shared/lib/auth.ts",
|
|
9
|
+
"destination": "server/auth/auth.ts"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"type": "create-file",
|
|
13
|
-
"source": "api/auth/[...nextauth]/route.ts",
|
|
13
|
+
"source": "nextjs/api/auth/[...nextauth]/route.ts",
|
|
14
14
|
"destination": "app/api/auth/[...nextauth]/route.ts"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"type": "create-file",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"source": "nextjs/proxy.ts",
|
|
19
|
+
"destination": "proxy.ts"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"type": "patch-file",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"operations": [
|
|
26
26
|
{
|
|
27
27
|
"type": "add-to-bottom",
|
|
28
|
-
"source": "prisma/schema.prisma"
|
|
28
|
+
"source": "shared/prisma/schema.prisma"
|
|
29
29
|
}
|
|
30
30
|
]
|
|
31
31
|
},
|
|
@@ -41,13 +41,23 @@
|
|
|
41
41
|
"envVars": {
|
|
42
42
|
"AUTH_SECRET": "",
|
|
43
43
|
"AUTH_GOOGLE_ID": "",
|
|
44
|
-
"AUTH_GOOGLE_SECRET": ""
|
|
44
|
+
"AUTH_GOOGLE_SECRET": "",
|
|
45
|
+
"EMAIL_HOST": "smtp.gmail.com",
|
|
46
|
+
"EMAIL_PORT": "587",
|
|
47
|
+
"EMAIL_USER": "",
|
|
48
|
+
"EMAIL_PASS": "",
|
|
49
|
+
"EMAIL_FROM": "noreply@yourapp.com"
|
|
45
50
|
}
|
|
46
51
|
},
|
|
47
52
|
{
|
|
48
53
|
"type": "add-dependency",
|
|
49
54
|
"dependencies": {
|
|
50
|
-
"next-auth": "^5.0.0-beta.30"
|
|
55
|
+
"next-auth": "^5.0.0-beta.30",
|
|
56
|
+
"bcryptjs": "^3.0.3",
|
|
57
|
+
"nodemailer": "^7.0.12"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/nodemailer": "^7.0.5"
|
|
51
61
|
}
|
|
52
62
|
}
|
|
53
63
|
]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from "express";
|
|
2
|
+
import { auth } from "../../modules/auth/auth";
|
|
3
|
+
|
|
4
|
+
export enum UserRole {
|
|
5
|
+
USER = "USER",
|
|
6
|
+
ADMIN = "ADMIN"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const authorize = (...roles: UserRole[]) => {
|
|
10
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
11
|
+
try {
|
|
12
|
+
// get user session
|
|
13
|
+
const session = await auth?.api.getSession({
|
|
14
|
+
headers: req.headers as any,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (!session) {
|
|
18
|
+
return res.status(401).json({
|
|
19
|
+
success: false,
|
|
20
|
+
message: "You are not authorized!",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!session.user.emailVerified) {
|
|
25
|
+
return res.status(403).json({
|
|
26
|
+
success: false,
|
|
27
|
+
message: "Email verification required. Please verify your email!",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
req.user = {
|
|
32
|
+
id: session.user.id,
|
|
33
|
+
email: session.user.email,
|
|
34
|
+
name: session.user.name,
|
|
35
|
+
role: session.user.role as string,
|
|
36
|
+
emailVerified: session.user.emailVerified,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (roles.length && !roles.includes(req.user.role as UserRole)) {
|
|
40
|
+
return res.status(403).json({
|
|
41
|
+
success: false,
|
|
42
|
+
message:
|
|
43
|
+
"Forbidden! You don't have permission to access this resources!",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
next();
|
|
48
|
+
} catch (err) {
|
|
49
|
+
next(err);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default authorize;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { auth } from "@/lib/auth";
|
|
2
|
+
import { headers } from "next/headers";
|
|
3
|
+
import { redirect } from "next/navigation";
|
|
4
|
+
|
|
5
|
+
export async function getSession() {
|
|
6
|
+
const session = await auth.api.getSession({
|
|
7
|
+
headers: await headers(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
return session;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function requireAdmin() {
|
|
14
|
+
const session = await getSession();
|
|
15
|
+
|
|
16
|
+
if (!session || session.user.role !== "ADMIN") {
|
|
17
|
+
redirect("/login");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return session.user;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function getUser() {
|
|
24
|
+
const session = await getSession();
|
|
25
|
+
|
|
26
|
+
if (!session) {
|
|
27
|
+
redirect("/login");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return session.user;
|
|
31
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextResponse, type NextRequest } from "next/server";
|
|
2
|
+
|
|
3
|
+
function isAuthenticated(req: NextRequest) {
|
|
4
|
+
const token = req.cookies.get("better-auth.session_token")?.value;
|
|
5
|
+
|
|
6
|
+
return Boolean(token);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function proxy(req: NextRequest) {
|
|
10
|
+
const { pathname, search } = req.nextUrl;
|
|
11
|
+
const authed = isAuthenticated(req);
|
|
12
|
+
|
|
13
|
+
if (pathname === "/login" || pathname === "/signup") {
|
|
14
|
+
if (authed) return NextResponse.redirect(new URL("/dashboard", req.url));
|
|
15
|
+
return NextResponse.next();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (pathname.startsWith("/dashboard") && !authed) {
|
|
19
|
+
const next = encodeURIComponent(pathname + (search || ""));
|
|
20
|
+
return NextResponse.redirect(new URL(`/login?next=${next}`, req.url));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return NextResponse.next();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const config = {
|
|
27
|
+
matcher: [
|
|
28
|
+
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
|
|
29
|
+
"/(api|trpc)(.*)",
|
|
30
|
+
"/login",
|
|
31
|
+
"/signup",
|
|
32
|
+
"/dashboard/:path*",
|
|
33
|
+
],
|
|
34
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createAuthClient } from "better-auth/react";
|
|
2
2
|
|
|
3
3
|
export const authClient = createAuthClient({
|
|
4
|
-
baseURL: "http://localhost:
|
|
4
|
+
baseURL: process.env.BETTER_AUTH_URL || "http://localhost:4000",
|
|
5
5
|
});
|
|
6
6
|
|
|
7
7
|
export const { signIn, signUp, signOut, useSession } = authClient;
|
|
@@ -1,35 +1,54 @@
|
|
|
1
1
|
import { betterAuth } from "better-auth";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
{
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
{{#if combo == "prisma:express"}}
|
|
3
|
+
import { sendEmail } from "../../shared/email/email-service";
|
|
4
|
+
import {
|
|
5
|
+
getPasswordResetEmailTemplate,
|
|
6
|
+
getVerificationEmailTemplate,
|
|
7
|
+
} from "../../shared/email/email-templates";
|
|
8
|
+
import { prisma } from "../../database/prisma";
|
|
7
9
|
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
8
|
-
{{/
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
{{/if}}
|
|
11
|
+
|
|
12
|
+
{{#if combo == "prisma:nextjs"}}
|
|
13
|
+
import { getPasswordResetEmailTemplate, getVerificationEmailTemplate } from "../service/email/email-templates";
|
|
14
|
+
import { sendEmail } from "../service/email/email-service";
|
|
15
|
+
import { prisma } from "../database/prisma";
|
|
16
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
17
|
+
{{/if}}
|
|
18
|
+
|
|
19
|
+
{{#if combo == "mongoose:express"}}
|
|
20
|
+
import { sendEmail } from "../../shared/email/email-service";
|
|
21
|
+
import {
|
|
22
|
+
getPasswordResetEmailTemplate,
|
|
23
|
+
getVerificationEmailTemplate,
|
|
24
|
+
} from "../../shared/email/email-templates";
|
|
25
|
+
import { mongoose } from "../../database/mongoose";
|
|
26
|
+
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
27
|
+
{{/if}}
|
|
28
|
+
|
|
29
|
+
{{#if combo == "mongoose:nextjs"}}
|
|
30
|
+
import { getPasswordResetEmailTemplate, getVerificationEmailTemplate } from "../service/email/email-templates";
|
|
31
|
+
import { sendEmail } from "../service/email/email-service";
|
|
32
|
+
import { mongoose } from "../database/mongoose";
|
|
11
33
|
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
12
|
-
{{/
|
|
13
|
-
{{/switch}}
|
|
34
|
+
{{/if}}
|
|
14
35
|
|
|
15
36
|
export async function initAuth() {
|
|
16
|
-
{{#if database ==
|
|
37
|
+
{{#if database == "mongoose"}}
|
|
17
38
|
const mongooseInstance = await mongoose();
|
|
18
39
|
const client = mongooseInstance.connection.getClient();
|
|
19
40
|
const db = client.db();
|
|
20
41
|
{{/if}}
|
|
21
42
|
|
|
22
43
|
return betterAuth({
|
|
23
|
-
{{#
|
|
24
|
-
{{#case prisma}}
|
|
44
|
+
{{#if database == "prisma"}}
|
|
25
45
|
database: prismaAdapter(prisma, {
|
|
26
46
|
provider: "{{prismaProvider}}",
|
|
27
47
|
}),
|
|
28
|
-
{{/
|
|
29
|
-
{{#
|
|
48
|
+
{{/if}}
|
|
49
|
+
{{#if database == "mongoose"}}
|
|
30
50
|
database: mongodbAdapter(db, { client }),
|
|
31
|
-
{{/
|
|
32
|
-
{{/switch}}
|
|
51
|
+
{{/if}}
|
|
33
52
|
baseURL: process.env.BETTER_AUTH_URL,
|
|
34
53
|
secret: process.env.BETTER_AUTH_SECRET,
|
|
35
54
|
trustedOrigins: [process.env.APP_URL!],
|
|
@@ -57,8 +76,10 @@ return betterAuth({
|
|
|
57
76
|
},
|
|
58
77
|
socialProviders: {
|
|
59
78
|
google: {
|
|
60
|
-
|
|
61
|
-
|
|
79
|
+
accessType: "offline",
|
|
80
|
+
prompt: "select_account consent",
|
|
81
|
+
clientId: process.env.GOOGLE_CLIENT_ID as string,
|
|
82
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
|
|
62
83
|
},
|
|
63
84
|
},
|
|
64
85
|
emailVerification: {
|
|
@@ -95,4 +116,9 @@ return betterAuth({
|
|
|
95
116
|
})
|
|
96
117
|
};
|
|
97
118
|
|
|
98
|
-
export
|
|
119
|
+
export let auth: any = undefined;
|
|
120
|
+
|
|
121
|
+
export async function setupAuth() {
|
|
122
|
+
auth = await initAuth();
|
|
123
|
+
return auth;
|
|
124
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
{{#
|
|
1
|
+
{{#if prismaProvider == "mongodb"}}
|
|
2
|
+
{{#var defaultId = @default(auto()) @map("_id") @db.ObjectId}}
|
|
3
|
+
{{else}}
|
|
4
|
+
{{#var defaultId = @default(cuid())}}
|
|
5
|
+
{{/if}}
|
|
2
6
|
model User {
|
|
3
7
|
id String @id {{defaultId}}
|
|
4
8
|
name String
|
|
@@ -9,7 +13,7 @@ model User {
|
|
|
9
13
|
updatedAt DateTime @updatedAt
|
|
10
14
|
sessions Session[]
|
|
11
15
|
accounts Account[]
|
|
12
|
-
role
|
|
16
|
+
role Role @default(USER)
|
|
13
17
|
|
|
14
18
|
@@unique([email])
|
|
15
19
|
@@map("user")
|
|
@@ -61,3 +65,8 @@ model Verification {
|
|
|
61
65
|
@@index([identifier])
|
|
62
66
|
@@map("verification")
|
|
63
67
|
}
|
|
68
|
+
|
|
69
|
+
enum Role {
|
|
70
|
+
USER
|
|
71
|
+
ADMIN
|
|
72
|
+
}
|
|
@@ -5,33 +5,81 @@
|
|
|
5
5
|
"operations": [
|
|
6
6
|
{
|
|
7
7
|
"type": "create-file",
|
|
8
|
-
"source": "lib/auth.ts",
|
|
9
|
-
"destination": "
|
|
10
|
-
"condition": { "framework": ["nextjs"
|
|
8
|
+
"source": "shared/lib/auth.ts",
|
|
9
|
+
"destination": "server/auth/auth.ts",
|
|
10
|
+
"condition": { "framework": ["nextjs"] }
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"type": "create-file",
|
|
14
|
-
"source": "lib/
|
|
15
|
-
"destination": "
|
|
16
|
-
"condition": { "framework": ["
|
|
14
|
+
"source": "shared/lib/auth.ts",
|
|
15
|
+
"destination": "src/modules/auth/auth.ts",
|
|
16
|
+
"condition": { "framework": ["express"] }
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"type": "create-file",
|
|
20
|
-
"source": "lib/email-
|
|
21
|
-
"destination": "
|
|
22
|
-
"condition": { "framework": ["nextjs"
|
|
20
|
+
"source": "shared/lib/email/email-service.ts",
|
|
21
|
+
"destination": "server/service/email/email-service.ts",
|
|
22
|
+
"condition": { "framework": ["nextjs"] }
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"type": "create-file",
|
|
26
|
-
"source": "
|
|
26
|
+
"source": "shared/lib/email/email-service.ts",
|
|
27
|
+
"destination": "src/shared/email/email-service.ts",
|
|
28
|
+
"condition": { "framework": ["express"] }
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "create-file",
|
|
32
|
+
"source": "shared/lib/email/email-templates.ts",
|
|
33
|
+
"destination": "server/service/email/email-templates.ts",
|
|
34
|
+
"condition": { "framework": ["nextjs"] }
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "create-file",
|
|
38
|
+
"source": "shared/lib/email/email-templates.ts",
|
|
39
|
+
"destination": "src/shared/email/email-templates.ts",
|
|
40
|
+
"condition": { "framework": ["express"] }
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"type": "create-file",
|
|
44
|
+
"source": "nextjs/api/auth/[...all]/route.ts",
|
|
27
45
|
"destination": "app/api/auth/[...all]/route.ts",
|
|
28
46
|
"condition": { "framework": "nextjs" }
|
|
29
47
|
},
|
|
30
48
|
{
|
|
31
49
|
"type": "create-file",
|
|
32
|
-
"source": "
|
|
33
|
-
"destination": "
|
|
34
|
-
"condition": { "framework":
|
|
50
|
+
"source": "nextjs/proxy.ts",
|
|
51
|
+
"destination": "proxy.ts",
|
|
52
|
+
"condition": { "framework": "nextjs" }
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"type": "create-file",
|
|
56
|
+
"source": "nextjs/lib/auth/auth-guards.ts",
|
|
57
|
+
"destination": "server/auth/guards.ts",
|
|
58
|
+
"condition": { "framework": "nextjs" }
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"type": "create-file",
|
|
62
|
+
"source": "express/middlewares/authorize.ts",
|
|
63
|
+
"destination": "src/shared/middlewares/authorize.middleware.ts",
|
|
64
|
+
"condition": { "framework": "express" }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "create-file",
|
|
68
|
+
"source": "express/types/express.d.ts",
|
|
69
|
+
"destination": "src/types/express.d.ts",
|
|
70
|
+
"condition": { "framework": "express" }
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"type": "create-file",
|
|
74
|
+
"source": "shared/lib/auth-client.ts",
|
|
75
|
+
"destination": "lib/auth/auth-client.ts",
|
|
76
|
+
"condition": { "framework": ["nextjs"] }
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"type": "create-file",
|
|
80
|
+
"source": "shared/lib/auth-client.ts",
|
|
81
|
+
"destination": "src/shared/lib/auth-client.ts",
|
|
82
|
+
"condition": { "framework": ["react"] }
|
|
35
83
|
},
|
|
36
84
|
{
|
|
37
85
|
"type": "patch-file",
|
|
@@ -40,7 +88,7 @@
|
|
|
40
88
|
"operations": [
|
|
41
89
|
{
|
|
42
90
|
"type": "add-to-bottom",
|
|
43
|
-
"source": "prisma/schema.prisma"
|
|
91
|
+
"source": "shared/prisma/schema.prisma"
|
|
44
92
|
}
|
|
45
93
|
]
|
|
46
94
|
},
|
|
@@ -51,12 +99,12 @@
|
|
|
51
99
|
"operations": [
|
|
52
100
|
{
|
|
53
101
|
"type": "add-import",
|
|
54
|
-
"imports": ["import {
|
|
102
|
+
"imports": ["import { setupAuth } from \"./modules/auth/auth\";"]
|
|
55
103
|
},
|
|
56
104
|
{
|
|
57
105
|
"type": "add-code",
|
|
58
106
|
"before": "const port = env.port;",
|
|
59
|
-
"code": "await
|
|
107
|
+
"code": "await setupAuth();"
|
|
60
108
|
}
|
|
61
109
|
]
|
|
62
110
|
},
|
|
@@ -67,13 +115,20 @@
|
|
|
67
115
|
"operations": [
|
|
68
116
|
{
|
|
69
117
|
"type": "add-import",
|
|
70
|
-
"imports": ["import { auth } from \"
|
|
118
|
+
"imports": ["import { auth } from \"./modules/auth/auth\";",
|
|
71
119
|
"import { toNodeHandler } from \"better-auth/node\";"]
|
|
72
120
|
},
|
|
73
121
|
{
|
|
74
122
|
"type": "add-code",
|
|
75
|
-
"after": "// routes",
|
|
76
|
-
"code":
|
|
123
|
+
"after": "// API routes",
|
|
124
|
+
"code": [
|
|
125
|
+
"app.all(\"/api/auth/*splat\", (req: Request, res: Response) => {",
|
|
126
|
+
" if (!auth) {",
|
|
127
|
+
" return res.status(503).json({ success: false, message: \"Auth not ready\" });",
|
|
128
|
+
" }",
|
|
129
|
+
" return toNodeHandler(auth)(req, res);",
|
|
130
|
+
"});"
|
|
131
|
+
]
|
|
77
132
|
}
|
|
78
133
|
]
|
|
79
134
|
},
|