diff --git a/.dockerignore b/.dockerignore index d6cbf7d6f..e59439d80 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,4 +12,8 @@ LICENCE **/build screenshots/ -server/public/images/* \ No newline at end of file +server/public/images/* + +# Existing entries... +traefik/ +docker in progress/ \ No newline at end of file diff --git a/.env.example b/.env.example index 89084d39e..65e46ab7a 100644 --- a/.env.example +++ b/.env.example @@ -47,3 +47,7 @@ CLIENT_URL_PROD=https://mern-boilerplate.amd2.localhost3002.live SERVER_URL_PROD=https://mern-boilerplate.amd2.localhost3002.live PORT=80 + +# Redis +REDIS_HOST=localhost +REDIS_PORT=6379 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 56c3f1ff7..332ef0507 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,52 @@ - +# Client and Server dependencies client/node_modules server/node_modules +# Log files client/npm-debug.log* +server/npm-debug.log* +**/logs/ +**/*.log +# Build outputs client/build +server/build +dist/ +# Ignore environment files but keep examples **/.env* !**/.env*.example -# ignore mongo data +# Ignore MongoDB data but keep .gitkeep !server/docker/mongo-data server/docker/mongo-data/* !server/docker/mongo-data/.gitkeep +# OS generated files +**/.DS_Store +**/Thumbs.db + +# IDE/editor settings +.vscode/ +.idea/ + +# Coverage reports +coverage/ + +# PM2 logs and process IDs +pids/ +*.pid +*.seed + +# Docker-related +docker-compose.override.yml +traefik/ +docker in progress/ +Dockerfile.backup +**/.docker-temp + +# Optional: system-level junk or cache +*.swp +*.swo +*.bak +*.tmp diff --git a/README.md b/README.md index aff383437..a9b777f4d 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,77 @@ # MERN Boilerplate -This is full stack boilerplate with React, Redux, Express, Mongoose and Passport. Skip the tedious part and get straight to developing your app. +A full-stack MERN boilerplate built with React, Redux, Express, MongoDB, Passport, and Docker. Skip setup hassle and start building your app. -## Demo +## 🚀 Live Demo -- Live demo is available here: **[https://mern-boilerplate.arm1.nemanjamitic.com](https://mern-boilerplate.arm1.nemanjamitic.com)** +👉 [View Live](https://mern-boilerplate.arm1.nemanjamitic.com) -## Deployment with Docker (2023. update) +--- -Since Heroku is no longer free I made Docker production deployment that you can use on any Linux VPS. +## 🧰 Tech Stack -- original [mern-docker-prod](https://github.com/nemanjam/mern-docker-prod) repository with Docker code and instructions that you can reuse to deploy your own Mern apps -- Traefik part of the deployment [traefik-proxy](https://github.com/nemanjam/traefik-proxy) and [traefik-proxy/apps/mern-boilerplate](https://github.com/nemanjam/traefik-proxy/tree/main/apps/mern-boilerplate) +- **Frontend**: React, Redux, Thunk, Formik, Yup +- **Backend**: Node.js, Express, MongoDB, Mongoose +- **Auth**: Passport (Local, Google, Facebook), JWT +- **Deployment**: Docker, Heroku +- **Other**: Babel, Joi Validation, Multer (Image Upload), PM2 + +--- + +## ⚙️ Features + +### Server + +- User & Message Models with 1:N relation +- Full REST API with CRUD operations +- Role-based Auth [User, Admin] +- Local & OAuth [Google, Facebook] Authentication +- JWT Protected Routes +- Image Uploads with Multer +- Server-side validation with Joi +- Seed DB with sample data +- `.env` file for config + +### Client +- React Functional Components with Hooks +- Redux for state management with Thunk +- Auth-protected routes with different views +- Admin dashboard with control access +- Form Validation using Formik & Yup +- Reusable layout & loader components +- Modular structure + +--- + +## 📦 Folder Structure + +```bash +mern-boilerplate/ +│ +├── client/ # React frontend +│ └── ... +│ +├── server/ # Node.js backend +│ └── src/ +│ ├── models/ +│ ├── routes/ +│ ├── controllers/ +│ └── ... +│ +├── screenshots/ # UI previews +├── .env.example # Sample config +└── docker-compose.yml -## Features -- Server +## Deployment with Docker (2023. update) - - User and Message models with `1:N` relation - - Full CRUD REST API operations for both Message and User models - - Passport authentication with local `email/password`, Facebook and Google OAuth strategies and JWT protected APIs - - `User` and `Admin` roles - - NodeJS server with Babel for new JS syntax unified with React client - - `async/await` syntax across whole app - - Joi server side validation of user's input - - Single `.env` file configuration - - Image upload with Multer - - Database seed +Since Heroku is no longer free I made Docker production deployment that you can use on any Linux VPS. + +- original [mern-docker-prod](https://github.com/nemanjam/mern-docker-prod) repository with Docker code and instructions that you can reuse to deploy your own Mern apps +- Traefik part of the deployment [traefik-proxy](https://github.com/nemanjam/traefik-proxy) and [traefik-proxy/apps/mern-boilerplate](https://github.com/nemanjam/traefik-proxy/tree/main/apps/mern-boilerplate) -- Client - - React client with functional components and Hooks - - Redux state management with Thunk for async actions - - CSS agnostic, so you don't waste your time replacing my CSS framework with yours - - Home, Users, Profile, Admin, Notfound, Login and Register pages - - Protected routes with Higher order components - - Different views for unauthenticated, authenticated and admin user - - Edit/Delete forms for Message and User with Formik and Yup validation - - Admin has privileges to edit and delete other users and their messages - - Layout component, so you can have pages without Navbar - - Loading states with Loader component - - Single config file within `/constants` folder ## Installation @@ -120,6 +150,12 @@ $ cd client $ npm install $ npm start ``` +## 🔧 Docker Build (ARM-compatible) + +### Client (React App) +```bash +docker build -t your-client-name -f ./client/Dockerfile . + That's it as far for development setup. For production check the `Deployment on Heroku` section. @@ -245,3 +281,5 @@ export const GOOGLE_AUTH_LINK = "https://my-own-app.herokuapp.com/auth/google"; ## Licence ### MIT + + diff --git a/client/package.json b/client/package.json index 0b2e68058..cc2582022 100644 --- a/client/package.json +++ b/client/package.json @@ -6,7 +6,8 @@ "start": "set HTTPS=true&&react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "docker:build-arm": "docker buildx build --platform linux/arm64 -t mern-client-arm . --load" }, "dependencies": { "@testing-library/jest-dom": "^4.2.4", diff --git a/docker-compose.yml b/docker-compose.yml index 7fd154f83..e91513690 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,41 +1,19 @@ version: '3.8' -# alternative 1 - inject separate env files into containers -# env_file: -# - ./client/.env -# env_file: -# - ./server/.env -# -# alternative 2 - put all vars into single .env and forward vars through docker-compose.yml -# environment: ... - services: mdp-client: container_name: mdp-client restart: unless-stopped image: nemanjamitic/mern-boilerplate-client:latest build: - # src must be in context context: ./client dockerfile: docker/Dockerfile depends_on: - mdp-server networks: - internal-client - environment: - - NODE_ENV=production - # forwarded - - REACT_APP_BASE_URL - # env_file: - # - ./client/.env - - # nginx ports - # either open port or route through traefik - # ports: - # - '3080:80' - # - '3443:443' # must edit nginx.conf - - # handles everything except /api and /public/images + env_file: + - ./client/.env labels: - 'traefik.enable=true' - 'traefik.docker.network=proxy' @@ -53,33 +31,13 @@ services: dockerfile: docker/Dockerfile depends_on: - mdp-mongo - environment: - - NODE_ENV=production - # forwarded - - MONGO_URI_PROD - - GOOGLE_CLIENT_ID - - GOOGLE_CLIENT_SECRET - - GOOGLE_CALLBACK_URL - - FACEBOOK_APP_ID - - FACEBOOK_SECRET - - FACEBOOK_CALLBACK_URL - - JWT_SECRET_PROD - - CLIENT_URL_PROD - - SERVER_URL_PROD - # env_file: - # - ./server/.env + env_file: + - ./server/.env volumes: - ./server/public/images:/home/node/app/server/public/images networks: - default - internal-client - - # either open port or route through traefik - # ports: - # - '5000' - - # must be exposed through traefik too - # handles /api and /public/images labels: - 'traefik.enable=true' - 'traefik.docker.network=proxy' @@ -93,14 +51,9 @@ services: container_name: mdp-mongo restart: unless-stopped ports: - - '27017' - environment: - # forwarded - - MONGO_INITDB_DATABASE - - MONGO_INITDB_ROOT_USERNAME - - MONGO_INITDB_ROOT_PASSWORD - # env_file: - # - ./server/.env + - '27017:27017' + env_file: + - ./server/.env volumes: - ./server/docker/mongo-data:/data/db networks: diff --git a/docs/git-foundations-notes-by-tayyiba-kiran.md b/docs/git-foundations-notes-by-tayyiba-kiran.md new file mode 100644 index 000000000..f4b02a2ad --- /dev/null +++ b/docs/git-foundations-notes-by-tayyiba-kiran.md @@ -0,0 +1,87 @@ + +## 🚀 Project Setup Notes + +### 📁 Folder Structure + +``` +/ +├── client/ # Frontend (React) +│ └── Dockerfile +├── server/ # Backend (Node.js + PM2) +│ └── Dockerfile +├── docker-compose.yml +├── .env.example +├── .gitignore +├── .dockerignore +├── README.md +└── package.json +``` + +--- + +### ✅ What I Updated + +#### 1. **Dockerfile (Client)** + +* Used Node to build the React app. +* Used Nginx to serve the static files. +* Final image is very small and fast. + +#### 2. **Dockerfile (Server)** + +* Used Node 16 Alpine image. +* Installed PM2 globally to run the server. +* Used `pm2-runtime` in the `CMD`. + +#### 3. **.gitignore** + +* Ignored: + + * node\_modules + * build/ + * env files (except example) + * log files + * editor and OS-specific files + +#### 4. **.env.example** + +* Added placeholder values for both client and server environments. + +#### 5. **docker-compose.yml** + +* Added setup for `client` and `server` services. +* Linked everything to work together. + +#### 6. **package.json** + +* Added scripts to build Docker images for ARM (Apple Silicon, Raspberry Pi etc): + +```json +"scripts": { + "docker:build:client:arm": "docker buildx build --platform linux/arm64 -t mdp-client-arm ./client", + "docker:build:server:arm": "docker buildx build --platform linux/arm64 -t mdp-server-arm ./server" +} +``` + +--- + +### 💻 Git Commands Used + +```bash +git add . +git commit -m "Updated Dockerfiles, envs, ignore files and added ARM build script" +git push +``` + +--- + +by Tayyiba Kiran + + + + + + + + x + diff --git a/package.json b/package.json index e85d571e2..8f18d25e2 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "docker:prod:arm:client:build": "COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 DOCKER_DEFAULT_PLATFORM=linux/arm64 docker compose build mdp-client", "docker:prod:arm:server:build": "COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 DOCKER_DEFAULT_PLATFORM=linux/arm64 docker compose build mdp-server", "docker:prod:client:push": "docker push nemanjamitic/mern-boilerplate-client:latest", - "docker:prod:server:push": "docker push nemanjamitic/mern-boilerplate-server:latest" + "docker:prod:server:push": "docker push nemanjamitic/mern-boilerplate-server:latest", + "docker:build:arm": "docker buildx build --platform linux/arm64 -t your-image-name:arm ." }, "author": "Nemanja Mitic (https://github.com/nemanjam)", "license": "MIT", diff --git a/screenshots/Screenshot-tayyiba-kiran.jpg b/screenshots/Screenshot-tayyiba-kiran.jpg new file mode 100644 index 000000000..f586707ea Binary files /dev/null and b/screenshots/Screenshot-tayyiba-kiran.jpg differ diff --git a/server/docker/Dockerfile b/server/docker/Dockerfile index b7fffebae..ca4ed3e54 100644 --- a/server/docker/Dockerfile +++ b/server/docker/Dockerfile @@ -1,30 +1,40 @@ FROM node:16.19.0-alpine +# Set non-root user ENV USER=node -# home folder node user -RUN mkdir -p /home/node/app +# Create app directory and set permissions +RUN mkdir -p /home/node/app && \ + mkdir /home/node/.npm-global && \ + chown -R node:node /home/node + WORKDIR /home/node/app -# relative to build context -COPY . ./ +# Copy package files first for better Docker caching COPY package*.json ./ -RUN chown -R node:node /home/node/app - -RUN mkdir /home/node/.npm-global -RUN chown -R node:node /home/node/.npm-global - +# Set up global npm path ENV PATH=/home/node/.npm-global/bin:$PATH ENV NPM_CONFIG_PREFIX=/home/node/.npm-global +# Switch to non-root user +USER node + +# Set npm global user config RUN npm --global config set user "${USER}" -USER node -RUN npm i --location=global pm2 +# Install global PM2 +RUN npm install --location=global pm2 -ENV NODE_ENV=production +# Install project dependencies RUN npm install + +# Copy rest of the app +COPY --chown=node:node . . + +# Build the app +ENV NODE_ENV=production RUN npm run build -CMD [ "pm2-runtime", "--no-auto-exit", "npm", "--", "run", "docker:start:prod" ] +# Run using PM2 +CMD ["pm2-runtime", "--no-auto-exit", "npm", "--", "run", "docker:start:prod"]