diff --git a/.env.example b/.env.example index fcacc5f..8eb254a 100644 --- a/.env.example +++ b/.env.example @@ -23,7 +23,9 @@ SMTP_PORT=587 SMTP_USERNAME=email-server-username SMTP_PASSWORD=email-server-password EMAIL_FROM=support@yourapp.com -FRONT_URL=http://localhost:5000 # Cookie configs COOKIE_SECRET=thisisasamplesecret + +# URL of client application +CLIENT_URL=http://localhost:5000 diff --git a/.eslintignore b/.eslintignore index ed4598e..cff71ae 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ node_modules bin +data diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 1b66333..3dc5932 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -18,7 +18,7 @@ env: JWT_REFRESH_EXPIRATION_DAYS: 30 JWT_RESET_PASSWORD_EXPIRATION_MINUTES: 10 JWT_VERIFY_EMAIL_EXPIRATION_MINUTES: 10 - FRONT_URL: http://localhost:3000 + CLIENT_URL: http://localhost:3000 jobs: diff --git a/.gitignore b/.gitignore index f50eab4..2c58d04 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,8 @@ yarn-error.log # Code coverage coverage -# bunlde folder -dist \ No newline at end of file +# build folder +dist + +# docker volume +data \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 6895bf0..161501f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,3 @@ node_modules coverage - +data diff --git a/Dockerfile b/Dockerfile index 1d5af63..d92946c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,26 @@ -FROM node:alpine +# development stage +FROM node:14-alpine as base -RUN mkdir -p /usr/dist/node-app && chown -R node:node /usr/dist/node-app +WORKDIR /usr/src/app -WORKDIR /usr/dist/node-app +COPY package.json yarn.lock tsconfig.json ecosystem.config.json ./ -COPY package.json yarn.lock ./ +COPY ./src ./src -USER node +RUN ls -a -RUN yarn install --pure-lockfile +RUN yarn install --pure-lockfile && yarn compile -COPY --chown=node:node . . +# production stage -EXPOSE 3000 +FROM base as production + +WORKDIR /usr/prod/app + +ENV NODE_ENV=production + +COPY package.json yarn.lock ecosystem.config.json ./ + +RUN yarn install --production --pure-lockfile + +COPY --from=base /usr/src/app/dist ./dist diff --git a/README.md b/README.md index 297d0d4..d9a1b19 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,9 @@ SMTP_PORT=587 SMTP_USERNAME=email-server-username SMTP_PASSWORD=email-server-password EMAIL_FROM=support@yourapp.com -FRONT_URL=http://localhost:5000 + +# URL of client application +CLIENT_URL=http://localhost:5000 ``` ## Project Structure diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index a14d287..32c6075 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,6 +1,14 @@ version: '3' services: - node-app: - container_name: node-app-dev + mongo: + volumes: + - ./data:/data/db + + app: + container_name: ts-node-app-dev command: yarn dev -L + +volumes: + mongo-data: + driver: local diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index d53fe52..ec462c4 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,6 +1,8 @@ version: '3' services: - node-app: - container_name: node-app-prod + app: + container_name: ts-node-app-prod + build: + target: production command: yarn start diff --git a/docker-compose.test.yml b/docker-compose.test.yml index e06adaf..f450cac 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -1,6 +1,6 @@ version: '3' services: - node-app: - container_name: node-app-test - command: yarn test + app: + container_name: ts-node-app-test + command: yarn test:js diff --git a/docker-compose.yml b/docker-compose.yml index 16a6e04..c7767d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,32 +1,36 @@ version: '3' services: - node-app: - build: . - image: node-app - environment: - - MONGODB_URL=mongodb://mongodb:27017/node-app + mongo: + container_name: mongo + image: mongo:4.2.1-bionic + restart: always ports: - - '3000:3000' - depends_on: - - mongodb - volumes: - - .:/usr/dist/node-app + - "27018:27017" networks: - - node-app-network - - mongodb: - image: mongo:4.2.1-bionic + - backend + app: + container_name: ts-node-app + build: + context: . + dockerfile: Dockerfile + target: base + restart: always + env_file: .env + expose: + - ${PORT} ports: - - '27017:27017' - volumes: - - dbdata:/data/db + - ${PORT}:${PORT} + environment: + - MONGODB_URL=mongodb://mongo:27017/node-boilerplate + - CLIENT_URL=${CLIENT_URL} + links: + - mongo + depends_on: + - mongo networks: - - node-app-network - -volumes: - dbdata: + - backend networks: - node-app-network: - driver: bridge + backend: + driver: bridge diff --git a/package.json b/package.json index e1219f9..78a3b68 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "node": ">=14.0.0" }, "scripts": { - "start": "tsc --build && pm2 start ecosystem.config.json --no-daemon", + "start": "pm2 start ecosystem.config.json --no-daemon", + "start:build": "tsc --build && pm2 start ecosystem.config.json --no-daemon", "compile": "tsc --build", "compile:watch": "tsc --build --watch", "pre:dev": "cross-env NODE_ENV=development nodemon --experimental-modules --es-module-specifier-resolution=node dist/index.js", diff --git a/src/config/config.ts b/src/config/config.ts index 564d6ad..14233b3 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -20,7 +20,7 @@ const envVarsSchema = Joi.object() SMTP_USERNAME: Joi.string().description('username for email server'), SMTP_PASSWORD: Joi.string().description('password for email server'), EMAIL_FROM: Joi.string().description('the from field in the emails sent by the app'), - FRONT_URL: Joi.string().required().description('Front url'), + CLIENT_URL: Joi.string().required().description('Client url'), }) .unknown(); @@ -64,7 +64,7 @@ const config = { }, from: envVars.EMAIL_FROM, }, - frontUrl: envVars.FRONT_URL, + clientUrl: envVars.CLIENT_URL, }; export default config; diff --git a/src/modules/email/email.service.ts b/src/modules/email/email.service.ts index 6745614..e1584d3 100644 --- a/src/modules/email/email.service.ts +++ b/src/modules/email/email.service.ts @@ -40,7 +40,7 @@ export const sendEmail = async (to: string, subject: string, text: string, html: export const sendResetPasswordEmail = async (to: string, token: string): Promise => { const subject = 'Reset password'; // replace this url with the link to the reset password page of your front-end app - const resetPasswordUrl = `http://${config.frontUrl}/reset-password?token=${token}`; + const resetPasswordUrl = `http://${config.clientUrl}/reset-password?token=${token}`; const text = `Hi, To reset your password, click on this link: ${resetPasswordUrl} If you did not request any password resets, then ignore this email.`; @@ -62,7 +62,7 @@ export const sendResetPasswordEmail = async (to: string, token: string): Promise export const sendVerificationEmail = async (to: string, token: string, name: string): Promise => { const subject = 'Email Verification'; // replace this url with the link to the email verification page of your front-end app - const verificationEmailUrl = `http://${config.frontUrl}/verify-email?token=${token}`; + const verificationEmailUrl = `http://${config.clientUrl}/verify-email?token=${token}`; const text = `Hi ${name}, To verify your email, click on this link: ${verificationEmailUrl} If you did not create an account, then ignore this email.`; @@ -82,7 +82,7 @@ export const sendVerificationEmail = async (to: string, token: string, name: str export const sendSuccessfulRegistration = async (to: string, token: string, name: string): Promise => { const subject = 'Email Verification'; // replace this url with the link to the email verification page of your front-end app - const verificationEmailUrl = `http://${config.frontUrl}/verify-email?token=${token}`; + const verificationEmailUrl = `http://${config.clientUrl}/verify-email?token=${token}`; const text = `Hi ${name}, Congratulations! Your account has been created. You are almost there. Complete the final step by verifying your email at: ${verificationEmailUrl} @@ -107,7 +107,7 @@ export const sendSuccessfulRegistration = async (to: string, token: string, name export const sendAccountCreated = async (to: string, name: string): Promise => { const subject = 'Account Created Successfully'; // replace this url with the link to the email verification page of your front-end app - const loginUrl = `http://${config.frontUrl}/auth/login`; + const loginUrl = `http://${config.clientUrl}/auth/login`; const text = `Hi ${name}, Congratulations! Your account has been created successfully. You can now login at: ${loginUrl}