diff --git a/README.md b/README.md index 31ba8f49..6315c84f 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ The app depends these libraries and frameworks. | COMMIT_ID | Default branch's commit sha related to the `RELEASE_VERSION` | | OPENGRAPH_IMAGE_URL | URL of a 1200x630 image to be used as an OpenGraph thumbnail | | BASE_URL | The base URL for the frontend of the documentation website. | + | WATCHPACK_POLLING | Enables hot reload on NextJS apps running inside Docker containers on a Windows host. Set it to true if running Docker Desktop with WSL2 on a Windows OS. | ## Usage @@ -50,8 +51,38 @@ The app depends these libraries and frameworks. cd docs npm run dev ``` -2. Add or edit MDX files in the `/pages` directory, or add React components in the `/components` directory. +2. (Optional) Run the app for local development using Docker. Navigate to the project's root directory then run:
+ ``` + # 2.1. Build the client and server containers for localhost development. + docker compose -f docker-compose.dev.yml build + + # 2.2. Create and start the development client and server containers + docker compose -f docker-compose.dev.yml up + + # 2.3. Stop and remove the development containers, networks, images and volumes + docker compose -f docker-compose.dev.yml down + ``` + +3. Add or edit MDX files in the `/pages` directory, or add React components in the `/components` directory. - View the [**nextra**](https://nextra.site/docs) (docs-theme) documentation for more information on using nextra to add content. +## Available Scripts + +### `npm run dev` + +Starts the local NextJS / Nextra app in development mode on localhost. + +### `npm run build` + +Builds the static site into the `"out"` directory. + +### `npm run lint` + +Checks lint errors. + +### `npm run lint:fix` + +Fixes lint errors. + @acaptutorials
20240806 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 00000000..7568bac4 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,25 @@ +version: "3" +services: + # NextJS v13 app running on development mode + acaptutorials.github.io-dev: + container_name: acaptutorials.github.io-dev + image: acaptutorials.github.io:dev + env_file: + - ./docs/.env + build: + context: ./docs + dockerfile: Dockerfile + target: development + networks: + - acaptutorials.github.io-dev + volumes: + - ./docs:/opt/docs + - /opt/docs/node_modules + - /opt/docs/.next + ports: + - "3000:3000" + +networks: + acaptutorials.github.io-dev: + name: acaptutorials.github.io-dev + external: false diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 00000000..e43183c1 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,22 @@ +version: "3" +services: + # NextJS exported app running on an nginx webserver + acaptutorials.github.io-prod: + container_name: acaptutorials.github.io-prod + image: acaptutorials.github.io:prod + restart: always + env_file: + - ./docs/.env + build: + context: ./docs + dockerfile: Dockerfile + target: production + networks: + - acaptutorials.github.io-prod + ports: + - "3000:3000" + +networks: + acaptutorials.github.io-prod: + name: acaptutorials.github.io-prod + external: false diff --git a/docs/.dockerignore b/docs/.dockerignore new file mode 100644 index 00000000..12eb063e --- /dev/null +++ b/docs/.dockerignore @@ -0,0 +1,6 @@ +.git +.gitignore +node_modules +npm-debug.log +Dockerfile +.dockerignore diff --git a/docs/.env.example b/docs/.env.example index eb4eee04..40df7584 100644 --- a/docs/.env.example +++ b/docs/.env.example @@ -2,4 +2,6 @@ RELEASE_VERSION=0.0.0-alpha.0 RELEASE_PAGE=https://github.com///releases/tag/0.0.0-alpha.0 COMMIT_ID=123456 OPENGRAPH_IMAGE_URL=https:///banner.png -BASE_URL=https://localhost:3000 \ No newline at end of file +BASE_URL=https://localhost:3000 +# Uncomment these 2 CHOKIDAR lines if using Docker Desktop and WSL2 on Windows OS +# WATCHPACK_POLLING=true \ No newline at end of file diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 00000000..94c8aa0e --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,29 @@ +FROM node:20.15.0-alpine as base +RUN mkdir -p /opt/docs +WORKDIR /opt/docs +RUN adduser -S client +RUN chown -R client /opt/docs +COPY package*.json ./ + +# BUILD TARGET +FROM base as build +RUN npm install && npm cache clean --force +COPY . ./ +RUN npm run export +USER client + +# DEVELOPMENT CLIENT PROFILE +FROM base as development +ENV NODE_ENV=development +RUN npm install && npm cache clean --force +COPY . ./ +EXPOSE 3000 +CMD ["npm", "run", "dev"] + +# PRODUCTION CLIENT PROFILE +FROM nginx:1.22.0-alpine as production +COPY --from=build /opt/docs/out /usr/share/nginx/html +RUN rm /etc/nginx/conf.d/default.conf +COPY config/nginx/nginx.conf /etc/nginx/conf.d +EXPOSE 3000 +CMD ["nginx", "-g", "daemon off;"] diff --git a/docs/config/nginx/nginx.conf b/docs/config/nginx/nginx.conf new file mode 100644 index 00000000..017dc104 --- /dev/null +++ b/docs/config/nginx/nginx.conf @@ -0,0 +1,29 @@ +# Minimal nginx configuration for running locally in containers +server { + listen 3000; + + root /usr/share/nginx/html; + include /etc/nginx/mime.types; + index index.html index.html; + + server_name localhost; + server_tokens off; + + # Rewrite all React URLs/routes to index.html + # location / { + # try_files $uri $uri/ /index.html =404; + # } + + # Reverse proxy to the backend API server + # Requires the backend service running on a container named 'http://acaptutorials.github.io-prod' + # location /api { + # proxy_pass http://acaptutorials.github.io-prod:3001; + # proxy_set_header Host $host; + # } + + # Other error pages + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/docs/config/nginx/nginx.full.conf b/docs/config/nginx/nginx.full.conf new file mode 100644 index 00000000..132efa63 --- /dev/null +++ b/docs/config/nginx/nginx.full.conf @@ -0,0 +1,103 @@ +# Full nginx configuration with SSL certificate for nginx running on host machine +# Requires a registered domain name, letsencrypt SSL certificates +# and local client/server apps (running in containers or manually installed on host) + +server { + listen 80; + listen [::]:80; + server_name www.; + return 301 https://$request_uri; +} + +server { + listen 80; + listen [::]:80; + server_name ; + return 301 https://$request_uri; +} + +server { + listen 443 ssl; + server_name www.; + ssl_certificate /etc/letsencrypt/live//fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live//privkey.pem; + return 301 https://$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name ; + server_tokens off; + + # Available methods + add_header Allow 'GET, POST, PATCH, DELETE, HEAD' always; + add_header X-XSS-Protection '1; mode=block'; + + if ( $request_method !~ ^(GET|POST|PATCH|DELETE|HEAD)$ ) { + return 405; + } + + ssl_certificate /etc/letsencrypt/live//fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live//privkey.pem; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; + ssl_dhparam '/etc/pki/nginx/dhparams.pem'; + + add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains' always; + + # gzip comppression settings + gzip on; + gzip_disable 'msie6'; + + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_min_length 0; + gzip_types text/plain application/javascript text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype; + + # Reverse proxy to the client website + # Requires the client service running on http://:3000 (from a container or manually installed on host) + location / { + proxy_pass http://:3000; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_cache_bypass $http_upgrade; + + # For websockets + proxy_http_version 1.1; + proxy_set_header Connection 'upgrade'; + proxy_set_header Upgrade $http_upgrade; + proxy_read_timeout 600s; + } + + # Reverse proxy to the backend API server + # Requires the backend service running on http://:3001 (from a container or manually installed on host) + location /api { + proxy_pass http://:3001; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_cache_bypass $http_upgrade; + + # For websockets + proxy_http_version 1.1; + proxy_set_header Connection 'upgrade'; + proxy_set_header Upgrade $http_upgrade; + proxy_read_timeout 600s; + } + + # Other error pages + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/scripts/docker-cleanup.sh b/scripts/docker-cleanup.sh new file mode 100644 index 00000000..f66b53bf --- /dev/null +++ b/scripts/docker-cleanup.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Stops and deletes ALL Docker resources +docker image prune +docker rmi $(docker images -a -q) +docker stop $(docker ps -a -q) +docker rm $(docker ps -a -q) +docker system prune -f +docker system prune -a +docker volume prune -f