stackkit 0.2.6 → 0.2.7

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.
Files changed (83) hide show
  1. package/dist/cli/create.js +1 -2
  2. package/dist/cli/list.js +73 -83
  3. package/dist/index.js +25 -44
  4. package/dist/lib/constants.d.ts +110 -0
  5. package/dist/lib/constants.js +112 -0
  6. package/dist/lib/env/env-editor.js +41 -47
  7. package/dist/lib/fs/files.d.ts +0 -1
  8. package/dist/lib/fs/files.js +12 -40
  9. package/dist/lib/generation/code-generator.d.ts +4 -1
  10. package/dist/lib/generation/code-generator.js +36 -10
  11. package/dist/lib/pm/package-manager.d.ts +1 -1
  12. package/dist/lib/pm/package-manager.js +130 -14
  13. package/dist/lib/ui/logger.d.ts +8 -1
  14. package/dist/lib/ui/logger.js +60 -3
  15. package/dist/lib/utils/fs-helpers.d.ts +12 -0
  16. package/dist/lib/utils/fs-helpers.js +61 -0
  17. package/dist/lib/utils/json-loader.d.ts +6 -0
  18. package/dist/lib/utils/json-loader.js +34 -0
  19. package/dist/lib/utils/module-loader.d.ts +9 -0
  20. package/dist/lib/utils/module-loader.js +98 -0
  21. package/dist/lib/utils/package-root.d.ts +1 -0
  22. package/dist/lib/utils/package-root.js +75 -2
  23. package/dist/lib/utils/path-resolver.d.ts +9 -0
  24. package/dist/lib/utils/path-resolver.js +44 -0
  25. package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +3 -0
  26. package/modules/auth/authjs/files/nextjs/proxy.ts +1 -0
  27. package/modules/auth/authjs/files/shared/lib/auth.ts +119 -0
  28. package/modules/auth/authjs/files/{prisma → shared/prisma}/schema.prisma +11 -1
  29. package/modules/auth/authjs/generator.json +18 -8
  30. package/modules/auth/better-auth/files/express/middlewares/authorize.ts +54 -0
  31. package/modules/auth/better-auth/files/express/types/express.d.ts +16 -0
  32. package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +31 -0
  33. package/modules/auth/better-auth/files/nextjs/proxy.ts +34 -0
  34. package/modules/auth/better-auth/files/{lib → shared/lib}/auth-client.ts +1 -1
  35. package/modules/auth/better-auth/files/{lib → shared/lib}/auth.ts +46 -20
  36. package/modules/auth/better-auth/files/{prisma → shared/prisma}/schema.prisma +11 -2
  37. package/modules/auth/better-auth/generator.json +74 -19
  38. package/modules/database/mongoose/generator.json +16 -2
  39. package/modules/database/prisma/files/lib/prisma.ts +1 -1
  40. package/modules/database/prisma/files/prisma/schema.prisma +1 -2
  41. package/modules/database/prisma/generator.json +8 -1
  42. package/package.json +2 -2
  43. package/templates/express/env.example +2 -1
  44. package/templates/express/package.json +3 -4
  45. package/templates/express/src/app.ts +18 -25
  46. package/templates/express/src/config/cors.ts +12 -0
  47. package/templates/express/src/config/helmet.ts +5 -0
  48. package/templates/express/src/config/logger.ts +6 -0
  49. package/templates/express/src/config/rate-limit.ts +11 -0
  50. package/templates/express/src/{features → modules}/health/health.route.ts +1 -1
  51. package/templates/express/src/routes/index.ts +12 -0
  52. package/templates/express/src/shared/errors/api-error.ts +14 -0
  53. package/templates/express/src/shared/errors/error-codes.ts +9 -0
  54. package/templates/express/src/shared/logger/logger.ts +20 -0
  55. package/templates/express/src/{middlewares → shared/middlewares}/error.middleware.ts +1 -1
  56. package/templates/express/src/shared/middlewares/not-found.middleware.ts +9 -0
  57. package/templates/express/src/shared/utils/async-handler.ts +9 -0
  58. package/templates/express/src/shared/utils/pagination.ts +6 -0
  59. package/templates/express/src/shared/utils/response.ts +9 -0
  60. package/templates/express/tsconfig.json +9 -3
  61. package/templates/react/src/app/layouts/dashboard-layout.tsx +8 -0
  62. package/templates/react/src/app/layouts/public-layout.tsx +5 -0
  63. package/templates/react/src/app/providers.tsx +20 -0
  64. package/templates/react/src/app/router.tsx +21 -0
  65. package/templates/react/src/{pages/About.tsx → features/about/pages/about.tsx} +1 -1
  66. package/templates/react/src/{pages/Home.tsx → features/home/pages/home.tsx} +1 -1
  67. package/templates/react/src/main.tsx +2 -2
  68. package/templates/react/src/{api/client.ts → shared/api/http.ts} +1 -1
  69. package/templates/react/src/{pages/NotFound.tsx → shared/pages/not-found.tsx} +1 -1
  70. package/dist/lib/git-utils.d.ts +0 -1
  71. package/dist/lib/git-utils.js +0 -29
  72. package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +0 -2
  73. package/modules/auth/authjs/files/lib/auth.ts +0 -22
  74. /package/modules/auth/better-auth/files/{api → nextjs/api}/auth/[...all]/route.ts +0 -0
  75. /package/modules/auth/better-auth/files/{lib → shared/lib/email}/email-service.ts +0 -0
  76. /package/modules/auth/better-auth/files/{lib → shared/lib/email}/email-templates.ts +0 -0
  77. /package/templates/express/src/{features → modules}/health/health.controller.ts +0 -0
  78. /package/templates/express/src/{features → modules}/health/health.service.ts +0 -0
  79. /package/templates/react/src/{components/ErrorBoundary.tsx → shared/components/error-boundary.tsx} +0 -0
  80. /package/templates/react/src/{components/Layout.tsx → shared/components/layout.tsx} +0 -0
  81. /package/templates/react/src/{components/Loading.tsx → shared/components/loading.tsx} +0 -0
  82. /package/templates/react/src/{components/SEO.tsx → shared/components/seo.tsx} +0 -0
  83. /package/templates/react/src/{lib/queryClient.ts → shared/lib/query-client.ts} +0 -0
@@ -5,18 +5,18 @@
5
5
  "operations": [
6
6
  {
7
7
  "type": "create-file",
8
- "source": "lib/auth.ts",
9
- "destination": "lib/auth.ts"
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
- "destination": "proxy.ts",
19
- "content": "export { auth as proxy } from \"@/lib/auth\""
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,16 @@
1
+ declare global {
2
+ namespace Express {
3
+ interface Request {
4
+ user?: {
5
+ id: string;
6
+ email: string;
7
+ name: string;
8
+ role: string;
9
+ emailVerified: boolean;
10
+ }
11
+ }
12
+ }
13
+ }
14
+
15
+ export { };
16
+
@@ -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:3000",
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
- import { sendEmail } from "./email/email-service";
3
- import { getVerificationEmailTemplate, getPasswordResetEmailTemplate } from "./email/email-templates";
4
- {{#switch database}}
5
- {{#case prisma}}
6
- import { prisma } from "./prisma";
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
- {{/case}}
9
- {{#case mongoose}}
10
- import { mongoose } from "./mongoose";
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
- {{/case}}
13
- {{/switch}}
34
+ {{/if}}
14
35
 
15
36
  export async function initAuth() {
16
- {{#if database == 'mongoose'}}
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
- {{#switch database}}
24
- {{#case prisma}}
44
+ {{#if database == "prisma"}}
25
45
  database: prismaAdapter(prisma, {
26
46
  provider: "{{prismaProvider}}",
27
47
  }),
28
- {{/case}}
29
- {{#case mongoose}}
48
+ {{/if}}
49
+ {{#if database == "mongoose"}}
30
50
  database: mongodbAdapter(db, { client }),
31
- {{/case}}
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
- clientId: process.env.GOOGLE_CLIENT_ID!,
61
- clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
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 const auth = await initAuth();
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
- {{#var defaultId = {{#if prismaProvider == "mongodb"}}@default(auto()) @map("_id") @db.ObjectId{{else}}@default(cuid()){{/if}}}}
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 String @default("USER")
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": "lib/auth.ts",
10
- "condition": { "framework": ["nextjs", "express"] }
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/email-service.ts",
15
- "destination": "lib/email/email-service.ts",
16
- "condition": { "framework": ["nextjs", "express"] }
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-templates.ts",
21
- "destination": "lib/email/email-templates.ts",
22
- "condition": { "framework": ["nextjs", "express"] }
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": "api/auth/[...all]/route.ts",
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": "lib/auth-client.ts",
33
- "destination": "lib/auth-client.ts",
34
- "condition": { "framework": ["nextjs", "react"] }
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 { initAuth } from \"../lib/auth\";"]
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 initAuth();"
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 \"../lib/auth\";",
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": "app.all(\"/api/auth/*splat\", toNodeHandler(auth));"
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
  },
@@ -6,12 +6,26 @@
6
6
  {
7
7
  "type": "create-file",
8
8
  "source": "lib/mongoose.ts",
9
- "destination": "lib/mongoose.ts"
9
+ "destination": "server/database/mongoose.ts",
10
+ "condition": { "framework": ["nextjs"] }
11
+ },
12
+ {
13
+ "type": "create-file",
14
+ "source": "lib/mongoose.ts",
15
+ "destination": "src/database/mongoose.ts",
16
+ "condition": { "framework": ["express"] }
17
+ },
18
+ {
19
+ "type": "create-file",
20
+ "source": "models/health.ts",
21
+ "destination": "src/modules/health/health.model.ts",
22
+ "condition": { "framework": ["express"] }
10
23
  },
11
24
  {
12
25
  "type": "create-file",
13
26
  "source": "models/health.ts",
14
- "destination": "models/health.ts"
27
+ "destination": "server/database/models/health.model.ts",
28
+ "condition": { "framework": ["nextjs"] }
15
29
  },
16
30
  {
17
31
  "type": "patch-file",
@@ -1,5 +1,5 @@
1
+ import { PrismaClient } from "@prisma/client";
1
2
  import 'dotenv/config';
2
- import { PrismaClient } from './generated/prisma/client'
3
3
 
4
4
  const globalForPrisma = globalThis as unknown as {
5
5
  prisma: PrismaClient | undefined
@@ -1,6 +1,5 @@
1
1
  generator client {
2
- provider = "prisma-client"
3
- output = "../lib/generated/prisma"
2
+ provider = "prisma-client-js"
4
3
  }
5
4
 
6
5
  datasource db {
@@ -16,7 +16,14 @@
16
16
  {
17
17
  "type": "create-file",
18
18
  "source": "lib/prisma.ts",
19
- "destination": "lib/prisma.ts"
19
+ "destination": "server/database/prisma.ts",
20
+ "condition": { "framework": ["nextjs"] }
21
+ },
22
+ {
23
+ "type": "create-file",
24
+ "source": "lib/prisma.ts",
25
+ "destination": "src/database/prisma.ts",
26
+ "condition": { "framework": ["express"] }
20
27
  },
21
28
  {
22
29
  "type": "add-dependency",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stackkit",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Production-ready CLI to create and extend JavaScript or TypeScript apps with modular stacks.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -80,4 +80,4 @@
80
80
  "@types/validate-npm-package-name": "^4.0.2",
81
81
  "typescript": "^5.3.3"
82
82
  }
83
- }
83
+ }
@@ -1,2 +1,3 @@
1
1
  PORT=3000
2
- NODE_ENV=development
2
+ NODE_ENV=development
3
+ CORS_ORIGIN="http://localhost:3000"
@@ -8,15 +8,15 @@
8
8
  "build": "tsc",
9
9
  "lint": "eslint src --ext .ts",
10
10
  "lint:fix": "eslint src --ext .ts --fix",
11
- "start": "node dist/server.js",
12
- "start:prod": "cross-env NODE_ENV=production node dist/server.js"
11
+ "start": "node dist/server.js"
13
12
  },
14
13
  "dependencies": {
15
14
  "cors": "^2.8.5",
16
15
  "dotenv": "^17.2.3",
17
16
  "express": "^5.2.1",
18
17
  "helmet": "^8.1.0",
19
- "morgan": "^1.10.1"
18
+ "morgan": "^1.10.1",
19
+ "express-rate-limit": "^6.10.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/cors": "^2.8.19",
@@ -25,7 +25,6 @@
25
25
  "@types/node": "^25.0.8",
26
26
  "@typescript-eslint/eslint-plugin": "^8.53.0",
27
27
  "@typescript-eslint/parser": "^8.53.0",
28
- "cross-env": "^10.1.0",
29
28
  "eslint": "^9.39.2",
30
29
  "tsx": "^4.21.0",
31
30
  "typescript": "^5.9.3"
@@ -1,28 +1,26 @@
1
- import cors from "cors";
2
- import express, { Application, NextFunction, Request, Response } from "express";
3
- import helmet from "helmet";
4
- import morgan from "morgan";
5
- import { env } from "./config/env";
6
- import { authRoutes } from "./features/health/health.route";
7
- import { errorHandler } from "./middlewares/error.middleware";
1
+ import express, { Application, Request, Response } from "express";
2
+ import { cors } from "./config/cors";
3
+ import { helmet } from "./config/helmet";
4
+ import { logger } from "./config/logger";
5
+ import { limiter } from "./config/rate-limit";
6
+ import { apiRoutes } from "./routes";
7
+ import { errorHandler } from "./shared/middlewares/error.middleware";
8
+ import { notFound } from "./shared/middlewares/not-found.middleware";
8
9
 
9
10
  // app initialization
10
11
  const app: Application = express();
11
12
  app.use(express.json());
12
13
 
13
- // security headers
14
- app.use(helmet());
14
+ app.use(helmet);
15
+ app.use(logger);
16
+ app.use(cors);
17
+ app.use(limiter);
15
18
 
16
- // logging
17
- if (env.isProduction) {
18
- app.use(morgan("combined"));
19
- } else {
20
- app.use(morgan("dev"));
19
+ // trust proxy when behind proxies (load balancers)
20
+ if (process.env.NODE_ENV === "production") {
21
+ app.set("trust proxy", 1);
21
22
  }
22
23
 
23
- // cors configuration
24
- app.use(cors());
25
-
26
24
  // Home page route
27
25
  app.get("/", (_req: Request, res: Response) => {
28
26
  res.status(200).json({
@@ -34,16 +32,11 @@ app.get("/", (_req: Request, res: Response) => {
34
32
  });
35
33
  });
36
34
 
37
- // routes
38
- app.use("/api/health", authRoutes);
35
+ // API routes
36
+ app.use("/api", apiRoutes);
39
37
 
40
38
  // unhandled routes
41
- app.use((req: Request, _res: Response, next: NextFunction) => {
42
- const error: any = new Error(`Can't find ${req.originalUrl} on this server!`);
43
- error.status = 404;
44
-
45
- next(error);
46
- });
39
+ app.use(notFound);
47
40
 
48
41
  // Global error handler
49
42
  app.use(errorHandler);
@@ -0,0 +1,12 @@
1
+ import createCors from "cors";
2
+ import { env } from "./env";
3
+
4
+ const origin = env.isProduction
5
+ ? process.env.CORS_ORIGIN
6
+ ? process.env.CORS_ORIGIN.split(",").map((s) => s.trim())
7
+ : false
8
+ : true;
9
+
10
+ const cors = createCors({ origin });
11
+
12
+ export { cors };
@@ -0,0 +1,5 @@
1
+ import createHelmet from "helmet";
2
+
3
+ const helmet = createHelmet();
4
+
5
+ export { helmet };