@anmol0493/fullstack-app 1.0.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/LICENSE +21 -0
- package/README.md +24 -0
- package/backend-express/.env.example +2 -0
- package/backend-express/index.js +2 -0
- package/backend-express/package-lock.json +1939 -0
- package/backend-express/package.json +25 -0
- package/backend-express/src/config/db.js +20 -0
- package/backend-express/src/controller/auth.js +71 -0
- package/backend-express/src/middleware/auth.js +36 -0
- package/backend-express/src/middleware/validator.js +16 -0
- package/backend-express/src/models/user.js +26 -0
- package/backend-express/src/routes/auth.js +25 -0
- package/backend-express/src/server.js +28 -0
- package/backend-express/src/utils/constants.js +14 -0
- package/backend-express/src/utils/helper.js +30 -0
- package/backend-express/src/utils/schema/auth.js +34 -0
- package/backend-nestjs/.env.example +3 -0
- package/backend-nestjs/.prettierrc +4 -0
- package/backend-nestjs/README.md +99 -0
- package/backend-nestjs/eslint.config.mjs +35 -0
- package/backend-nestjs/nest-cli.json +8 -0
- package/backend-nestjs/package.json +99 -0
- package/backend-nestjs/pnpm-lock.yaml +7848 -0
- package/backend-nestjs/src/app.controller.ts +12 -0
- package/backend-nestjs/src/app.module.ts +13 -0
- package/backend-nestjs/src/app.service.ts +8 -0
- package/backend-nestjs/src/common/decorators/user.decorator.ts +8 -0
- package/backend-nestjs/src/common/dtos/common.dto.ts +87 -0
- package/backend-nestjs/src/common/enum/index.ts +0 -0
- package/backend-nestjs/src/common/exceptions/custom.exception.ts +28 -0
- package/backend-nestjs/src/common/filters/http-exception.filter.ts +46 -0
- package/backend-nestjs/src/common/guard/permission.guard.ts +18 -0
- package/backend-nestjs/src/common/middleware/auth.middleware.ts +20 -0
- package/backend-nestjs/src/common/pipes/validation.pipe.ts +61 -0
- package/backend-nestjs/src/common/utils/constants.ts +36 -0
- package/backend-nestjs/src/common/utils/helper.ts +43 -0
- package/backend-nestjs/src/core/core.module.ts +8 -0
- package/backend-nestjs/src/core/jwt/jwt.module.ts +10 -0
- package/backend-nestjs/src/core/jwt/jwt.service.ts +45 -0
- package/backend-nestjs/src/core/prisma/prisma.module.ts +9 -0
- package/backend-nestjs/src/core/prisma/prisma.service.ts +17 -0
- package/backend-nestjs/src/core/prisma/schema.prisma +27 -0
- package/backend-nestjs/src/main.ts +26 -0
- package/backend-nestjs/src/module/auth/auth.controller.ts +30 -0
- package/backend-nestjs/src/module/auth/auth.module.ts +10 -0
- package/backend-nestjs/src/module/auth/auth.service.ts +83 -0
- package/backend-nestjs/src/module/auth/dto/index.ts +28 -0
- package/backend-nestjs/src/module/index.module.ts +17 -0
- package/backend-nestjs/src/scripts/migrate.js +40 -0
- package/backend-nestjs/tsconfig.build.json +4 -0
- package/backend-nestjs/tsconfig.json +22 -0
- package/frontend/.env.example +1 -0
- package/frontend/README.md +54 -0
- package/frontend/eslint.config.js +28 -0
- package/frontend/index.html +13 -0
- package/frontend/package-lock.json +3813 -0
- package/frontend/package.json +43 -0
- package/frontend/public/vite.svg +1 -0
- package/frontend/src/App.tsx +24 -0
- package/frontend/src/assets/react.svg +1 -0
- package/frontend/src/components/Layout.tsx +59 -0
- package/frontend/src/components/ui/AlertDialog.tsx +47 -0
- package/frontend/src/components/ui/Button.tsx +61 -0
- package/frontend/src/components/ui/CommonAlertDialog.tsx +57 -0
- package/frontend/src/components/ui/FormInput.tsx +73 -0
- package/frontend/src/components/ui/Loader.tsx +7 -0
- package/frontend/src/hook/useFetchUser.ts +38 -0
- package/frontend/src/index.css +1 -0
- package/frontend/src/lib/constants.ts +24 -0
- package/frontend/src/lib/schema.ts +12 -0
- package/frontend/src/lib/utils.ts +71 -0
- package/frontend/src/main.tsx +11 -0
- package/frontend/src/pages/Home.tsx +5 -0
- package/frontend/src/pages/Login.tsx +67 -0
- package/frontend/src/pages/Signup.tsx +67 -0
- package/frontend/src/redux/api/auth.ts +19 -0
- package/frontend/src/redux/slice/auth.ts +39 -0
- package/frontend/src/redux/store.ts +30 -0
- package/frontend/src/routes/index.tsx +20 -0
- package/frontend/src/routes/middleware.ts +18 -0
- package/frontend/src/types/index.ts +12 -0
- package/frontend/src/vite-env.d.ts +1 -0
- package/frontend/tsconfig.app.json +26 -0
- package/frontend/tsconfig.json +7 -0
- package/frontend/tsconfig.node.json +24 -0
- package/frontend/vite.config.ts +19 -0
- package/package.json +34 -0
- package/scripts/setup.js +73 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
2
|
+
import { SignupFormData } from "../types";
|
|
3
|
+
import { useForm } from "react-hook-form";
|
|
4
|
+
import { SignupSchema } from "../lib/schema";
|
|
5
|
+
import { AUTH_TOKEN, SignUpFields } from "../lib/constants";
|
|
6
|
+
import { FormInput } from "../components/ui/FormInput";
|
|
7
|
+
import { Button } from "../components/ui/Button";
|
|
8
|
+
import { Link } from "react-router-dom";
|
|
9
|
+
import { handleError, handleSuccess } from "../lib/utils";
|
|
10
|
+
import { useRegisterMutation } from "../redux/api/auth";
|
|
11
|
+
import { actions } from "../redux/store";
|
|
12
|
+
|
|
13
|
+
export default function Signup() {
|
|
14
|
+
const [registerApi, { isLoading }] = useRegisterMutation();
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
register,
|
|
18
|
+
handleSubmit,
|
|
19
|
+
formState: { errors }
|
|
20
|
+
} = useForm<SignupFormData>({
|
|
21
|
+
resolver: zodResolver(SignupSchema)
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const onSubmit = async (data: SignupFormData) => {
|
|
25
|
+
const { data: res, error } = await registerApi(data);
|
|
26
|
+
if (error) {
|
|
27
|
+
handleError(error);
|
|
28
|
+
} else {
|
|
29
|
+
localStorage.setItem(AUTH_TOKEN, res.token);
|
|
30
|
+
actions.auth.setToken(res.token);
|
|
31
|
+
handleSuccess(res);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return (
|
|
35
|
+
<div className="h-full flex items-center justify-center bg-white">
|
|
36
|
+
<div className="w-full sm:max-w-[500px] p-4 sm:p-8 sm:py-10 sm:shadow-md rounded-lg space-y-8">
|
|
37
|
+
<div className="text-center text-3xl font-bold text-gray-900">
|
|
38
|
+
Signup
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<form className="space-y-6" onSubmit={handleSubmit(onSubmit)}>
|
|
42
|
+
{SignUpFields.map(({ name, ...rest }) => (
|
|
43
|
+
<FormInput
|
|
44
|
+
key={name}
|
|
45
|
+
name={name}
|
|
46
|
+
register={register}
|
|
47
|
+
error={errors?.[name]?.message}
|
|
48
|
+
{...rest}
|
|
49
|
+
/>
|
|
50
|
+
))}
|
|
51
|
+
|
|
52
|
+
<Button
|
|
53
|
+
type="submit"
|
|
54
|
+
className="w-full text-md"
|
|
55
|
+
isLoading={isLoading}
|
|
56
|
+
>
|
|
57
|
+
Signup
|
|
58
|
+
</Button>
|
|
59
|
+
|
|
60
|
+
<Link to="/" className="text-[14px] text-blue-500 underline">
|
|
61
|
+
Already have an account? Sign in
|
|
62
|
+
</Link>
|
|
63
|
+
</form>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createApi } from "@reduxjs/toolkit/query/react";
|
|
2
|
+
import { baseQueryInterceptor } from "../../lib/utils";
|
|
3
|
+
|
|
4
|
+
export const authApi = createApi({
|
|
5
|
+
reducerPath: "authApi",
|
|
6
|
+
baseQuery: baseQueryInterceptor("/api/auth"),
|
|
7
|
+
endpoints: (builder) => ({
|
|
8
|
+
getUser: builder.query({ query: () => "/profile" }),
|
|
9
|
+
login: builder.mutation({
|
|
10
|
+
query: (body) => ({ url: "/login", method: "POST", body })
|
|
11
|
+
}),
|
|
12
|
+
register: builder.mutation({
|
|
13
|
+
query: (body) => ({ url: "/register", method: "POST", body })
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const { useLazyGetUserQuery, useLoginMutation, useRegisterMutation } =
|
|
19
|
+
authApi;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createSlice } from "@reduxjs/toolkit";
|
|
2
|
+
import { User } from "../../types";
|
|
3
|
+
import { AUTH_TOKEN } from "../../lib/constants";
|
|
4
|
+
|
|
5
|
+
interface AuthState {
|
|
6
|
+
currentUser: null | User;
|
|
7
|
+
loading: boolean;
|
|
8
|
+
token: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const initialState: AuthState = {
|
|
12
|
+
currentUser: null,
|
|
13
|
+
loading: false,
|
|
14
|
+
token: null
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const authSlice = createSlice({
|
|
18
|
+
name: "auth",
|
|
19
|
+
initialState,
|
|
20
|
+
reducers: {
|
|
21
|
+
setCurrentUser: (state, action) => {
|
|
22
|
+
state.currentUser = action.payload;
|
|
23
|
+
},
|
|
24
|
+
setIsLoading: (state, action) => {
|
|
25
|
+
state.loading = action.payload;
|
|
26
|
+
},
|
|
27
|
+
setToken: (state, action) => {
|
|
28
|
+
state.token = action.payload;
|
|
29
|
+
},
|
|
30
|
+
clearToken: (state) => {
|
|
31
|
+
localStorage.removeItem(AUTH_TOKEN);
|
|
32
|
+
state.currentUser = null;
|
|
33
|
+
state.token = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const { setIsLoading, setCurrentUser } = authSlice.actions;
|
|
39
|
+
export default authSlice.reducer;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { configureStore } from "@reduxjs/toolkit";
|
|
2
|
+
import authReducer, { authSlice } from "./slice/auth";
|
|
3
|
+
import { authApi } from "./api/auth";
|
|
4
|
+
|
|
5
|
+
export const store = configureStore({
|
|
6
|
+
reducer: {
|
|
7
|
+
auth: authReducer,
|
|
8
|
+
[authApi.reducerPath]: authApi.reducer
|
|
9
|
+
},
|
|
10
|
+
middleware: (getDefaultMiddleware) =>
|
|
11
|
+
getDefaultMiddleware().concat(authApi.middleware)
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export type RootState = ReturnType<typeof store.getState>;
|
|
15
|
+
export type AppDispatch = typeof store.dispatch;
|
|
16
|
+
|
|
17
|
+
const createActions = (slice: any) => {
|
|
18
|
+
const actions: any = {};
|
|
19
|
+
|
|
20
|
+
Object.keys(slice.actions).forEach((key) => {
|
|
21
|
+
actions[key] = (payload: any) =>
|
|
22
|
+
store.dispatch(slice.actions[key](payload));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return actions;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const actions = {
|
|
29
|
+
auth: createActions(authSlice)
|
|
30
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { lazy, Suspense } from "react";
|
|
2
|
+
import { Navigate, Route, Routes } from "react-router-dom";
|
|
3
|
+
import { Loader } from "../components/ui/Loader";
|
|
4
|
+
|
|
5
|
+
const Home = lazy(() => import("../pages/Home"));
|
|
6
|
+
const Login = lazy(() => import("../pages/Login"));
|
|
7
|
+
const Signup = lazy(() => import("../pages/Signup"));
|
|
8
|
+
|
|
9
|
+
export default function PageRoutes() {
|
|
10
|
+
return (
|
|
11
|
+
<Suspense fallback={<Loader />}>
|
|
12
|
+
<Routes>
|
|
13
|
+
<Route path="/" element={<Home />} />
|
|
14
|
+
<Route path="/login" element={<Login />} />
|
|
15
|
+
<Route path="/signup" element={<Signup />} />
|
|
16
|
+
<Route path="*" element={<Navigate to="/" replace />} />
|
|
17
|
+
</Routes>
|
|
18
|
+
</Suspense>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { useSelector } from 'react-redux'
|
|
3
|
+
import { useLocation, useNavigate } from 'react-router-dom'
|
|
4
|
+
import { RootState } from '../redux/store'
|
|
5
|
+
|
|
6
|
+
export const AuthHandler = () => {
|
|
7
|
+
const navigate = useNavigate()
|
|
8
|
+
const { pathname } = useLocation()
|
|
9
|
+
const { currentUser, loading } = useSelector((state: RootState) => state.auth)
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!loading) {
|
|
13
|
+
if (!currentUser && !['/login', '/signup'].includes(pathname)) navigate('/login')
|
|
14
|
+
else if (currentUser && ['/login', '/signup'].includes(pathname)) navigate('/')
|
|
15
|
+
}
|
|
16
|
+
}, [loading, currentUser, pathname])
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SigninSchema, SignupSchema } from "../lib/schema";
|
|
3
|
+
|
|
4
|
+
export interface User {
|
|
5
|
+
_id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
email: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type LoginFormData = z.infer<typeof SigninSchema>;
|
|
11
|
+
|
|
12
|
+
export type SignupFormData = z.infer<typeof SignupSchema>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"jsx": "react-jsx",
|
|
17
|
+
|
|
18
|
+
/* Linting */
|
|
19
|
+
"strict": true,
|
|
20
|
+
"noUnusedLocals": true,
|
|
21
|
+
"noUnusedParameters": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noUncheckedSideEffectImports": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["src"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"moduleDetection": "force",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
|
|
16
|
+
/* Linting */
|
|
17
|
+
"strict": true,
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noUncheckedSideEffectImports": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["vite.config.ts"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react-swc';
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
+
|
|
5
|
+
const apiUrl = import.meta.env.VITE_API_URL;
|
|
6
|
+
|
|
7
|
+
// https://vite.dev/config/
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
plugins: [react(), tailwindcss()],
|
|
10
|
+
server: {
|
|
11
|
+
proxy: {
|
|
12
|
+
'/api': {
|
|
13
|
+
target: apiUrl,
|
|
14
|
+
changeOrigin: true,
|
|
15
|
+
rewrite: (path) => path.replace(/^\/api/, '')
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anmol0493/fullstack-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A full-stack template with Vite React frontend and Express/NestJS backend options.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"setup": "node scripts/setup.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/Anmol0493/boilerplate.git"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"fullstack",
|
|
16
|
+
"vite",
|
|
17
|
+
"react",
|
|
18
|
+
"express",
|
|
19
|
+
"nestjs"
|
|
20
|
+
],
|
|
21
|
+
"author": "Anmol Dobariya",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/Anmol0493/boilerplate/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/Anmol0493/boilerplate#readme",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"enquirer": "^2.4.1",
|
|
29
|
+
"execa": "^9.5.2"
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"create-fullstack-app": "./scripts/setup.js"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/scripts/setup.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import enquirer from 'enquirer'; // Install enquirer: npm install enquirer
|
|
6
|
+
const { prompt } = enquirer;
|
|
7
|
+
import { execa } from 'execa'; // Install execa: npm install execa
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { dirname } from 'path';
|
|
10
|
+
|
|
11
|
+
// Derive __dirname for ESM
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Get the project name from the command-line arguments
|
|
16
|
+
const projectName = process.argv[2];
|
|
17
|
+
|
|
18
|
+
if (!projectName) {
|
|
19
|
+
console.error('Please provide a project name:');
|
|
20
|
+
console.error(' npx @anmol040903/fullstack-app <project-name>');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Define paths
|
|
25
|
+
const templateDir = path.join(__dirname, '..'); // Root directory of your template
|
|
26
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
27
|
+
|
|
28
|
+
// Ensure the target directory doesn't already exist
|
|
29
|
+
if (fs.existsSync(targetDir)) {
|
|
30
|
+
console.error(`Directory "${projectName}" already exists.`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
(async () => {
|
|
35
|
+
try {
|
|
36
|
+
// Step 1: Prompt the user to choose a backend
|
|
37
|
+
const response = await prompt({
|
|
38
|
+
type: 'select',
|
|
39
|
+
name: 'backend',
|
|
40
|
+
message: 'Which backend would you like to use?',
|
|
41
|
+
choices: ['Express', 'NestJS'],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const selectedBackend = response.backend.toLowerCase(); // e.g., "express" or "nestjs"
|
|
45
|
+
const backendSourceDir = path.join(templateDir, `backend-${selectedBackend}/`);
|
|
46
|
+
const backendTargetDir = path.join(targetDir, 'backend/');
|
|
47
|
+
|
|
48
|
+
// Step 2: Create the project directory and copy files
|
|
49
|
+
console.log(`Creating project "${projectName}"...`);
|
|
50
|
+
fs.mkdirSync(targetDir, { recursive: true }); // Create the project directory
|
|
51
|
+
|
|
52
|
+
// Copy frontend files
|
|
53
|
+
console.log('Copying frontend files...');
|
|
54
|
+
fs.cpSync(path.join(templateDir, 'frontend/'), path.join(targetDir, 'frontend/'), { recursive: true });
|
|
55
|
+
|
|
56
|
+
// Copy the selected backend files
|
|
57
|
+
console.log(`Setting up ${response.backend} backend...`);
|
|
58
|
+
fs.mkdirSync(backendTargetDir, { recursive: true });
|
|
59
|
+
fs.cpSync(backendSourceDir, backendTargetDir, { recursive: true });
|
|
60
|
+
|
|
61
|
+
// Step 3: Install dependencies for frontend and backend
|
|
62
|
+
console.log('Installing dependencies...');
|
|
63
|
+
process.chdir(targetDir); // Navigate to the new project directory
|
|
64
|
+
|
|
65
|
+
await execa('npm', ['install'], { cwd: path.join(targetDir, 'frontend'), stdio: 'inherit' });
|
|
66
|
+
await execa('npm', ['install'], { cwd: backendTargetDir, stdio: 'inherit' });
|
|
67
|
+
|
|
68
|
+
console.log(`Project ${projectName} created successfully!`);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('An error occurred during setup:', error);
|
|
71
|
+
process.exit(1); // Exit with a failure code
|
|
72
|
+
}
|
|
73
|
+
})();
|