diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..42fcd96
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,7 @@
+# ID for identifying containers created by docker-compose
+# This variable is valid for all projects (debug, hub, nginx, and "production")
+ID_PROJECT=mean
+
+# For angular, the base href is the path where the app is hosted, use / if the app is hosted in the root.
+# This is used to generate the index.html file with the correct base href and is only valid for production mode.
+BASE_HREF=/contacts/
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
index dfe0770..ade39d2 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,4 @@
# Auto detect text files and perform LF normalization
* text=auto
+# For windows users
+mongo/init-db.d/init-mongo.sh text eol=lf
\ No newline at end of file
diff --git a/README.md b/README.md
index b83f0f7..654f83f 100644
--- a/README.md
+++ b/README.md
@@ -106,7 +106,7 @@ Install latest [Docker Desktop](https://www.docker.com/products/docker-desktop)
docker-compose -f 'docker-compose.debug.yml' up
```
- It will run fronend `http://localhost:4200` and api on `http://localhost:3000`. you can also access mongodb on port 27017.
+ It will run frontend `http://localhost:4200` and api on `http://localhost:3000`. you can also access mongodb on port 27017.
Also, it will automatically refresh (hot reload) your UI for code changes. That is also true for expressjs file changes.
@@ -125,7 +125,7 @@ Install latest [Docker Desktop](https://www.docker.com/products/docker-desktop)
```
docker-compose up
```
- It will run fronend and api on `http://localhost:3000`. you can also access mongodb on port 27017
+ It will run frontend and api on `http://localhost:3000`. you can also access mongodb on port 27017
##### Using 4 containers (Mongo,api, angular and nginx)
```
git clone https://github.com/nitin27may/mean-docker.git
@@ -133,7 +133,7 @@ Install latest [Docker Desktop](https://www.docker.com/products/docker-desktop)
docker-compose -f 'docker-compose.nginx.yml' up
```
- It will run fronend and api on `http://localhost`. you can aslo access by it's invidual ports. For Frontend `http://localhost:4000` and for api `http://localhost:3000` .you can also access mongodb on port 27017
+ It will run frontend and api on `http://localhost`. you can aslo access by it's invidual ports. For Frontend `http://localhost:4000` and for api `http://localhost:3000` .you can also access mongodb on port 27017
#### About Docker Compose File
The main focus of this project to show case the possible way to run a real application (Mean stack) using docker.
diff --git a/api/api-routes.js b/api/api-routes.js
index 8d71c43..0d824f5 100644
--- a/api/api-routes.js
+++ b/api/api-routes.js
@@ -1,8 +1,13 @@
// Filename: api-routes.js
// Initialize express router
let router = require("express").Router();
+var { expressjwt: jwt } = require("express-jwt");
+const environment = require("./config/environment");
+
+const jwtAuth = jwt({ secret: environment.secret, algorithms: ["HS256"] });
+
// Set default API response
-router.get("/", function(req, res) {
+router.get("/", function (req, res) {
res.json({
status: "API Its Working",
message: "Welcome to RESTHub crafted with love!"
@@ -11,35 +16,38 @@ router.get("/", function(req, res) {
// Import user controller
var userController = require("./controllers/users.controller");
+
// user routes
router
.route("/users")
- .get(userController.index)
+ .get(jwtAuth, userController.index)
.post(userController.new);
router
.route("/user/:user_id")
- .get(userController.view)
- .patch(userController.update)
- .put(userController.update)
- .delete(userController.delete);
-router.route("/user/authenticate").post(userController.authenticate);
+ .get(jwtAuth, userController.view)
+ .patch(jwtAuth, userController.update)
+ .put(jwtAuth, userController.update)
+ .delete(jwtAuth, userController.delete);
router
.route("/user/changepassword/:user_id")
- .put(userController.changePassword);
+ .put(jwtAuth, userController.changePassword);
+// Public route for user authentication (without jwtAuth)
+router.route("/user/authenticate").post(userController.authenticate);
// Import Contact controller
var contactController = require("./controllers/contact.controller");
+
// Contact routes
router
.route("/contacts")
- .get(contactController.index)
- .post(contactController.new);
+ .get(jwtAuth, contactController.index)
+ .post(jwtAuth, contactController.new);
router
.route("/contact/:contact_id")
- .get(contactController.view)
- .patch(contactController.update)
- .put(contactController.update)
- .delete(contactController.delete);
+ .get(jwtAuth, contactController.view)
+ .patch(jwtAuth, contactController.update)
+ .put(jwtAuth, contactController.update)
+ .delete(jwtAuth, contactController.delete);
// Export API routes
module.exports = router;
diff --git a/api/server.js b/api/server.js
index 0bb52ba..e76ffa6 100644
--- a/api/server.js
+++ b/api/server.js
@@ -82,19 +82,14 @@ app.use(
}
}).unless({
path: [
- "/api/user/authenticate",
- "/api/users",
- "/index.html",
- "/*.js",
- "/*.css"
+ { url: "/api/user/authenticate", methods: ["POST"] },
+ { url: "/index.html", methods: ["GET"] },
+ { url: /\.js$/, methods: ["GET"] },
+ { url: /\.css$/, methods: ["GET"] }
]
})
);
-
-
-
-
const HOST = "0.0.0.0";
const port = Number(process.env.EXPRESS_PORT) || 3000;
// start server
diff --git a/docker-compose.debug.yml b/docker-compose.debug.yml
index 3f69f69..40815b9 100644
--- a/docker-compose.debug.yml
+++ b/docker-compose.debug.yml
@@ -6,10 +6,10 @@ services:
build: # specify the directory of the Dockerfile
context: ./frontend
dockerfile: debug.dockerfile
- command: ["npm", "run", "start"]
- container_name: mean_angular
+ command: sh -c "npm install && ng serve --host 0.0.0.0 --poll=2000 --disable-host-check"
+ container_name: ${ID_PROJECT:-mean}_angular
volumes:
- - ./frontend/src:/app/src
+ - ./frontend:/app
ports:
- "4200:4200" # specify port forewarding
- "49153:49153"
@@ -22,8 +22,8 @@ services:
build: # specify the directory of the Dockerfile
context: ./api
dockerfile: debug.dockerfile
- container_name: mean_express
- command : ["npm", "run", "dev:server"]
+ container_name: ${ID_PROJECT:-mean}_express
+ command: sh -c "npm install && npm run dev:server"
volumes:
- ./api:/api
ports:
@@ -32,6 +32,7 @@ services:
environment:
- SECRET=Thisismysecret
- NODE_ENV=development
+ - MONGO_INITDB_DATABASE=admin
- MONGO_DB_USERNAME=admin-user
- MONGO_DB_PASSWORD=admin-password
- MONGO_DB_HOST=database
@@ -43,7 +44,7 @@ services:
database: # name of the third service
image: mongo # specify image to build container from
- container_name: mean_mongo
+ container_name: ${ID_PROJECT:-mean}_mongo
environment:
- MONGO_INITDB_ROOT_USERNAME=admin-user
- MONGO_INITDB_ROOT_PASSWORD=admin-password
diff --git a/docker-compose.hub.yml b/docker-compose.hub.yml
index 9e6e0d5..1e5b7c9 100644
--- a/docker-compose.hub.yml
+++ b/docker-compose.hub.yml
@@ -4,7 +4,7 @@ version: "3.8" # specify docker-compose version
services:
angular: # name of the first service
image: nitin27may/mean-angular:latest # specify image to build container from
- container_name: mean_angular
+ container_name: ${ID_PROJECT:-mean}_angular
restart: always
ports:
- "4000:4000" # specify port forewarding
@@ -15,7 +15,7 @@ services:
express: #name of the second service
image: nitin27may/mean-expressjs:latest # specify image to build container from
- container_name: mean_express
+ container_name: ${ID_PROJECT:-mean}_express
restart: always
ports:
- "3000:3000" #specify ports forewarding
@@ -33,7 +33,7 @@ services:
database: # name of the third service
image: mongo:latest # specify image to build container from
- container_name: mean_mongo
+ container_name: ${ID_PROJECT:-mean}_mongo
restart: always
environment:
- MONGO_INITDB_ROOT_USERNAME=admin-user
@@ -50,7 +50,7 @@ services:
nginx: #name of the fourth service
image: nitin27may/mean-nginx # specify image to build container from
- container_name: mean_nginx
+ container_name: ${ID_PROJECT:-mean}_nginx
restart: always
ports:
- "80:80" #specify ports forewarding
diff --git a/docker-compose.nginx.yml b/docker-compose.nginx.yml
index b3a9fa5..f07ed75 100644
--- a/docker-compose.nginx.yml
+++ b/docker-compose.nginx.yml
@@ -4,7 +4,7 @@ version: "3.8" # specify docker-compose version
services:
angular: # name of the first service
build: frontend # specify the directory of the Dockerfile
- container_name: mean_angular
+ container_name: ${ID_PROJECT:-mean}_angular
restart: always
ports:
- "4000:4000" # specify port forewarding
@@ -13,7 +13,7 @@ services:
express: #name of the second service
build: api # specify the directory of the Dockerfile
- container_name: mean_express
+ container_name: ${ID_PROJECT:-mean}_express
restart: always
ports:
- "3000:3000" #specify ports forewarding
@@ -32,7 +32,7 @@ services:
database: # name of the third service
image: mongo:latest # specify image to build container from
- container_name: mean_mongo
+ container_name: ${ID_PROJECT:-mean}_mongo
restart: always
environment:
- MONGO_INITDB_ROOT_USERNAME=admin-user
@@ -49,7 +49,7 @@ services:
nginx: #name of the fourth service
build: loadbalancer # specify the directory of the Dockerfile
- container_name: mean_nginx
+ container_name: ${ID_PROJECT:-mean}_nginx
restart: always
ports:
- "80:80" #specify ports forewarding
diff --git a/docker-compose.yml b/docker-compose.yml
index ef5f8a2..d331143 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -6,7 +6,9 @@ services:
build: # specify the directory of the Dockerfile
context: .
dockerfile: dockerfile
- container_name: mean_angular_express
+ args:
+ BASE_HREF: ${BASE_HREF:-/}
+ container_name: ${ID_PROJECT:-mean}_angular_express
ports:
- "3000:3000" #specify ports forewarding
# Below database enviornment variable for api is helpful when you have to use database as managed service
@@ -23,7 +25,7 @@ services:
database: # name of the third service
image: mongo:latest # specify image to build container from
- container_name: mean_mongo
+ container_name: ${ID_PROJECT:-mean}_mongo
environment:
- MONGO_INITDB_ROOT_USERNAME=admin-user
- MONGO_INITDB_ROOT_PASSWORD=admin-password
diff --git a/dockerfile b/dockerfile
index d145b4e..a5ec716 100644
--- a/dockerfile
+++ b/dockerfile
@@ -11,9 +11,20 @@ WORKDIR /app
COPY /frontend .
+## Change apiEndpoint in environment.ts
+RUN sh -c "sed -i 's|http://localhost:3000/api|/api|' src/environments/environment.ts"
+
+## Change production to true in environment.ts
+RUN sh -c "sed -i 's|production: false|production: true|' src/environments/environment.ts"
+
+ARG BASE_HREF=/
+
## Build the angular app in production mode and store the artifacts in dist folder
RUN npm run build:prod
+## Change base href in index.html
+RUN sh -c "find . -name \"index*.html\" -exec sed -i 's|||' {} +"
+
### STAGE 2: Setup ###
FROM node:20-alpine
@@ -26,7 +37,8 @@ COPY /api/ /app/
RUN npm ci
## From ‘builder’ copy published angular bundles in app/public
-COPY --from=builder /app/dist /app/public
+COPY --from=builder /app/dist/contacts/browser /app/public
+
## expose port for express
EXPOSE 3000
diff --git a/frontend/angular.json b/frontend/angular.json
index 45daa07..71e99e6 100644
--- a/frontend/angular.json
+++ b/frontend/angular.json
@@ -45,8 +45,8 @@
"budgets": [
{
"type": "initial",
- "maximumWarning": "500kB",
- "maximumError": "1MB"
+ "maximumWarning": "4MB",
+ "maximumError": "5MB"
},
{
"type": "anyComponentStyle",
@@ -106,4 +106,4 @@
"cli": {
"analytics": "d893c76f-5030-492f-b845-f7f2d5c1a138"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/package.json b/frontend/package.json
index aeae3fd..3ef8aec 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,6 +6,7 @@
"start": "ng serve",
"serve": "ng serve --proxy-config proxy.conf.json",
"build": "ng build",
+ "build:prod": "ng build --configuration production",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"serve:ssr:contacts": "node dist/contacts/server/server.mjs"
@@ -51,4 +52,4 @@
"prettier-plugin-tailwindcss": "0.6.1",
"typescript": "~5.4.2"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/app/@core/interceptors/jwtToken.Interceptor.ts b/frontend/src/app/@core/interceptors/jwtToken.Interceptor.ts
new file mode 100644
index 0000000..a385642
--- /dev/null
+++ b/frontend/src/app/@core/interceptors/jwtToken.Interceptor.ts
@@ -0,0 +1,15 @@
+import { HttpInterceptorFn } from '@angular/common/http';
+
+export const jwtInterceptor: HttpInterceptorFn = (req, next) => {
+ // Obtén el token del localStorage
+ const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');
+ const token = currentUser?.token;
+
+ // Clona la solicitud y agrega el encabezado de autorización si existe el token
+ const authReq = token
+ ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })
+ : req;
+
+ // Pasa la solicitud al siguiente manejador
+ return next(authReq);
+};
diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts
index 8ad003c..fa24877 100644
--- a/frontend/src/app/app.config.ts
+++ b/frontend/src/app/app.config.ts
@@ -2,36 +2,38 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideAnimations } from '@angular/platform-browser/animations';
import { provideRouter } from '@angular/router';
-import { provideHttpClient } from '@angular/common/http';
+import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideClientHydration } from '@angular/platform-browser';
import { provideToastr } from 'ngx-toastr';
import { routes } from './app.routes';
import { provideErrorTailorConfig } from "./@core/components/validation";
+import { jwtInterceptor } from "./@core/interceptors/jwtToken.Interceptor";
export const appConfig: ApplicationConfig = {
- providers: [
- provideHttpClient(),
- provideZoneChangeDetection({ eventCoalescing: true }),
- provideRouter(routes),
- provideClientHydration(),
- provideAnimations(), // required animations providers
- provideToastr(), // Toastr providers
- provideErrorTailorConfig({
- errors: {
- useFactory() {
- return {
- required: 'This field is required',
- minlength: ({ requiredLength, actualLength }) => `Expect ${requiredLength} but got ${actualLength}`,
- invalidEmailAddress: error => `Email Address is not valid`,
- invalidMobile: error => `Invalid Mobile number`,
- invalidPassword: error => `Password is weak`,
- passwordMustMatch: error => `Password is not matching`,
- };
- },
- deps: []
- }
- //controlErrorComponent: CustomControlErrorComponent, // Uncomment to see errors being rendered using a custom component
- //controlErrorComponentAnchorFn: controlErrorComponentAnchorFn // Uncomment to see errors being positioned differently
- })
- ],
+ providers: [
+ provideHttpClient(),
+ provideZoneChangeDetection({ eventCoalescing: true }),
+ provideRouter(routes),
+ provideClientHydration(),
+ provideAnimations(), // required animations providers
+ provideToastr(), // Toastr providers
+ provideErrorTailorConfig({
+ errors: {
+ useFactory() {
+ return {
+ required: 'This field is required',
+ minlength: ({ requiredLength, actualLength }) => `Expect ${requiredLength} but got ${actualLength}`,
+ invalidEmailAddress: error => `Email Address is not valid`,
+ invalidMobile: error => `Invalid Mobile number`,
+ invalidPassword: error => `Password is weak`,
+ passwordMustMatch: error => `Password is not matching`,
+ };
+ },
+ deps: []
+ }
+ //controlErrorComponent: CustomControlErrorComponent, // Uncomment to see errors being rendered using a custom component
+ //controlErrorComponentAnchorFn: controlErrorComponentAnchorFn // Uncomment to see errors being positioned differently
+ }),
+ provideHttpClient(withInterceptors([jwtInterceptor])),
+ ],
};
diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts
index 2996ab6..9561200 100644
--- a/frontend/src/environments/environment.ts
+++ b/frontend/src/environments/environment.ts
@@ -1,8 +1,8 @@
export const environment = {
- production: false,
- apiEndpoint: '/api',
- angular: 'Angular 18',
- bootstrap: 'Bootstrap 5',
- expressjs: 'Express.js 4.17.1',
- mongoDb : 'MongoDB 7.0',
+ production: false,
+ apiEndpoint: 'http://localhost:3000/api',
+ angular: 'Angular 18',
+ bootstrap: 'Bootstrap 5',
+ expressjs: 'Express.js 4.17.1',
+ mongoDb: 'MongoDB 7.0',
};
diff --git a/mongo/init-db.d/01.Seed.sh b/mongo/init-db.d/01.Seed.no
similarity index 100%
rename from mongo/init-db.d/01.Seed.sh
rename to mongo/init-db.d/01.Seed.no
diff --git a/mongo/init-db.d/02.Users.sh b/mongo/init-db.d/02.Users.no
similarity index 100%
rename from mongo/init-db.d/02.Users.sh
rename to mongo/init-db.d/02.Users.no
diff --git a/mongo/init-db.d/init-mongo.sh b/mongo/init-db.d/init-mongo.sh
index 49c6731..a5faf2e 100644
--- a/mongo/init-db.d/init-mongo.sh
+++ b/mongo/init-db.d/init-mongo.sh
@@ -1,12 +1,15 @@
-mongo -- "$MONGO_INITDB_DATABASE" <