diff --git a/.env.example b/.env.example index 812412ceb..dc33dbb37 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,8 @@ +#NODE ENV +NODE_ENV=dev + +#----------- + #admin db user DB_ROOT_USER=admin diff --git a/.github/workflows/deploy_backend.yml b/.github/workflows/deploy_backend.yml new file mode 100644 index 000000000..16cd046de --- /dev/null +++ b/.github/workflows/deploy_backend.yml @@ -0,0 +1,28 @@ +name: Deploy backend + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: azure/docker-login@v1 + with: + login-server: divideandconquer.azurecr.io + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - run: | + docker build ./backend -t divideandconquer.azurecr.io/backend:latest + docker push divideandconquer.azurecr.io/backend:latest + + - uses: azure/webapps-deploy@v2 + with: + app-name: 'divideandconquer-be' + publish-profile: ${{ secrets.AZURE_BACKEND_PUBLISH_PROFILE }} + images: 'divideandconquer.azurecr.io/backend:latest' \ No newline at end of file diff --git a/.github/workflows/deploy_frontend.yml b/.github/workflows/deploy_frontend.yml new file mode 100644 index 000000000..0077988d5 --- /dev/null +++ b/.github/workflows/deploy_frontend.yml @@ -0,0 +1,28 @@ +name: Deploy frontend + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: azure/docker-login@v1 + with: + login-server: divideandconquer.azurecr.io + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - run: | + docker build ./frontend --build-arg NEXT_PUBLIC_BACKEND_URL=${{secrets.NEXT_PUBLIC_BACKEND_URL}} --build-arg NEXT_PUBLIC_NEXTAUTH_URL=${{secrets.NEXT_PUBLIC_NEXTAUTH_URL}} --build-arg NEXT_PUBLIC_EXPIRATION_TIME=${{secrets.NEXT_PUBLIC_EXPIRATION_TIME}} --build-arg NEXT_PUBLIC_ENABLE_AZURE=${{secrets.NEXT_PUBLIC_ENABLE_AZURE}} -t divideandconquer.azurecr.io/frontend:latest + docker push divideandconquer.azurecr.io/frontend:latest + + - uses: azure/webapps-deploy@v2 + with: + app-name: "divideandconquer" + publish-profile: ${{ secrets.AZURE_FRONTEND_PUBLISH_PROFILE }} + images: "divideandconquer.azurecr.io/frontend:latest" diff --git a/.github/workflows/tests_backend_frontend.yml b/.github/workflows/tests_backend_frontend.yml index a603ffd5b..2ba0ffa0d 100644 --- a/.github/workflows/tests_backend_frontend.yml +++ b/.github/workflows/tests_backend_frontend.yml @@ -39,12 +39,12 @@ jobs: key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} restore-keys: | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- - - name: npm ci, build and test + - name: npm ci and test run: | cd backend npm ci npm test - - name: npm ci, build and test frontend + - name: npm ci and test frontend run: | cd frontend npm ci diff --git a/.gitignore b/.gitignore index 938c97711..988476e62 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ node_modules .env.local .env.development .env.production +.env.dev +.env.staging diff --git a/backend/.env.example b/backend/.env.example index 7455efc68..43c0afb6b 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,4 +1,7 @@ +#NODE ENV +NODE_ENV=dev + #docker database container name DB_HOST=localhost diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js index 1d58c4cd4..2eebc07cf 100644 --- a/backend/.eslintrc.js +++ b/backend/.eslintrc.js @@ -32,5 +32,6 @@ module.exports = { '@typescript-eslint/naming-convention': 'off', '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-empty-interface' : 'off', + "prefer-destructuring": "off", }, }; diff --git a/backend/.gitignore b/backend/.gitignore index 9f35ea9aa..85f0740e2 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -41,3 +41,5 @@ lerna-debug.log* .env.test.local .env.production.local .env +.env.dev +.env.staging \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index f3c019978..ab1475415 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -8,13 +8,14 @@ COPY . /home/node RUN npm ci \ && npm run build \ - && npm prune --production + && npm prune --staging # --- FROM node:16-alpine -ENV NODE_ENV production +ARG NODE_ENV +ENV NODE_ENV=${NODE_ENV} EXPOSE 3200 diff --git a/backend/package.json b/backend/package.json index f5803ad96..783cd3263 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,7 +10,8 @@ "build": "nest build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "start": "nest start", - "start:dev": "nest start --watch", + "start:dev": "NODE_ENV=dev nest start --watch", + "start:staging": "NODE_ENV=staging nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", diff --git a/backend/src/infrastructure/config/config.module.ts b/backend/src/infrastructure/config/config.module.ts index 6f561aa87..3855d2835 100644 --- a/backend/src/infrastructure/config/config.module.ts +++ b/backend/src/infrastructure/config/config.module.ts @@ -3,18 +3,33 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import * as Joi from 'joi'; import { configuration } from './configuration'; +const NODE_ENV = process.env.NODE_ENV; + @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, + envFilePath: + !NODE_ENV || NODE_ENV === 'dev' ? '.env' : `.env.${NODE_ENV}`, load: [configuration], validationSchema: Joi.object({ + NODE_ENV: Joi.string() + .valid('dev', 'prod', 'test', 'staging') + .default('dev'), DB_HOST: Joi.string().required(), DB_USER: Joi.string().required(), DB_PASSWORD: Joi.string().required(), DB_NAME: Joi.string().required(), - DB_PORT: Joi.number().required(), - DB_REPLICA_SET: Joi.string().required(), + DB_PORT: Joi.any().when('NODE_ENV', { + is: 'dev', + then: Joi.required(), + otherwise: Joi.optional(), + }), + DB_REPLICA_SET: Joi.any().when('NODE_ENV', { + is: 'dev', + then: Joi.required(), + otherwise: Joi.optional(), + }), BACKEND_PORT: Joi.number().required(), JWT_ACCESS_TOKEN_SECRET: Joi.string().required(), JWT_ACCESS_TOKEN_EXPIRATION_TIME: Joi.string().required(), diff --git a/backend/src/infrastructure/config/configuration.ts b/backend/src/infrastructure/config/configuration.ts index 49dc405ac..f4521fde0 100644 --- a/backend/src/infrastructure/config/configuration.ts +++ b/backend/src/infrastructure/config/configuration.ts @@ -3,13 +3,17 @@ import { Configuration } from './interfaces/configuration.interface'; export const DEFAULT_SERVER_PORT = 3200; export const configuration = (): Configuration => { + const NODE_ENV = process.env.NODE_ENV; const defaultConfiguration = { server: { port: - parseInt(process.env.SERVER_PORT as string, 10) || DEFAULT_SERVER_PORT, + parseInt(process.env.BACKEND_PORT as string, 10) || DEFAULT_SERVER_PORT, }, database: { - uri: `mongodb://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}?authSource=admin&replicaSet=${process.env.DB_REPLICA_SET}&readPreference=primary&directConnection=true&ssl=false`, + uri: + NODE_ENV === 'dev' + ? `mongodb://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}?authSource=admin&replicaSet=${process.env.DB_REPLICA_SET}&readPreference=primary&directConnection=true` + : `mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}/${process.env.DB_NAME}?retryWrites=true&w=majority`, }, jwt: { accessToken: { diff --git a/docker-compose.yaml b/docker-compose.yaml index 9ccf0beb0..5c5ee701e 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -8,16 +8,22 @@ services: ports: - "3200:3200" environment: + NODE_ENV: ${NODE_ENV} DB_USER: ${DB_USER:-backend} DB_PASSWORD: ${DB_PASSWORD:-password} DB_NAME: ${DB_NAME:-dc} DB_HOST: ${DB_HOST:-mongo} DB_PORT: ${DB_PORT:-27017} + DB_REPLICA_SET: ${DB_REPLICA_SET:-dcrs} BACKEND_PORT: ${BACKEND_PORT:-3200} JWT_ACCESS_TOKEN_SECRET: ${JWT_ACCESS_TOKEN_SECRET:-backenddc2022} JWT_ACCESS_TOKEN_EXPIRATION_TIME: ${JWT_ACCESS_TOKEN_EXPIRATION_TIME:-900} JWT_REFRESH_TOKEN_SECRET: ${JWT_REFRESH_TOKEN_SECRET:-backenddc2022refresh} JWT_REFRESH_TOKEN_EXPIRATION_TIME: ${JWT_REFRESH_TOKEN_EXPIRATION_TIME:-1} + AZURE_CLIENT_ID: ${AZURE_CLIENT_ID} + AZURE_CLIENT_SECRET: ${AZURE_CLIENT_SECRET} + AZURE_TENANT_ID: ${AZURE_TENANT_ID} + AZURE_ENABLE: ${AZURE_ENABLE} restart: unless-stopped networks: - dc-network @@ -30,6 +36,7 @@ services: args: - NEXT_PUBLIC_BACKEND_URL=${NEXT_PUBLIC_BACKEND_URL:-http://localhost:3200} - NEXT_PUBLIC_NEXTAUTH_URL=${NEXT_PUBLIC_NEXTAUTH_URL:-http://localhost:3000} + - NEXT_PUBLIC_ENABLE_AZURE=${NEXT_PUBLIC_ENABLE_AZURE} image: dc-frontend ports: - "3000:3000" @@ -40,6 +47,10 @@ services: NEXT_PUBLIC_NEXTAUTH_URL: ${NEXT_PUBLIC_NEXTAUTH_URL:-http://localhost:3000} SECRET: ${SECRET:-56e9169e3383d4a73fef9e0b4a3ff4e2} NEXT_PUBLIC_EXPIRATION_TIME: ${NEXT_PUBLIC_EXPIRATION_TIME:-900} + AZURE_CLIENT_ID: ${AZURE_CLIENT_ID} + AZURE_CLIENT_SECRET: ${AZURE_CLIENT_SECRET} + AZURE_TENANT_ID: ${AZURE_TENANT_ID} + NEXT_PUBLIC_ENABLE_AZURE: ${NEXT_PUBLIC_ENABLE_AZURE} restart: unless-stopped networks: - dc-network diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 2f53e7362..066275886 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -16,6 +16,8 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . +ARG NEXT_PUBLIC_ENABLE_AZURE +ENV NEXT_PUBLIC_ENABLE_AZURE=${NEXT_PUBLIC_ENABLE_AZURE} ARG NEXT_PUBLIC_BACKEND_URL ENV NEXT_PUBLIC_BACKEND_URL=${NEXT_PUBLIC_BACKEND_URL} ARG NEXT_PUBLIC_NEXTAUTH_URL @@ -27,7 +29,8 @@ RUN npm run build FROM node:16-alpine AS runner WORKDIR /app -#ENV NODE_ENV production +ARG NODE_ENV +ENV NODE_ENV=${NODE_ENV} RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 diff --git a/frontend/pages/boards/[boardId].tsx b/frontend/pages/boards/[boardId].tsx index 604e74565..3c0da30dc 100644 --- a/frontend/pages/boards/[boardId].tsx +++ b/frontend/pages/boards/[boardId].tsx @@ -101,7 +101,9 @@ const Board: React.FC = () => { }, [board, data, dispatch, userId]); useEffect(() => { - const newSocket: Socket = io(NEXT_PUBLIC_BACKEND_URL ?? "http://127.0.0.1:3200"); + const newSocket: Socket = io(NEXT_PUBLIC_BACKEND_URL ?? "http://127.0.0.1:3200", { + transports: ["polling"], + }); newSocket.on("connect", () => { newSocket.emit("join", { boardId });