diff --git a/README.md b/README.md
index 47af781..8cf73d0 100644
--- a/README.md
+++ b/README.md
@@ -101,5 +101,19 @@ https://sites.google.com/view/gsites-embed-app/full-page
- Navigate to the /server directory from the commandline and run:
`npm run user:create --email=randomemail@gmail.com --password=anypasasword --displayname="Game Tester" --emailverified=true`
+## Run with Docker
+
+```
+# Run on development mode
+docker compose -f docker-compose.dev.yml build
+docker compose -f docker-compose.dev.yml up
+docker compose -f docker-compose.dev.yml down
+
+# Build and run for production mode
+docker compose -f docker-compose.prod.yml build
+docker compose -f docker-compose.prod.yml up
+docker compose -f docker-compose.prod.yml down
+```
+
@weaponsforge
20230326
diff --git a/client/.dockerignore b/client/.dockerignore
new file mode 100644
index 0000000..bf1062a
--- /dev/null
+++ b/client/.dockerignore
@@ -0,0 +1,9 @@
+.git
+.gitignore
+node_modules
+npm-debug.log
+Dockerfile
+.dockerignore
+*.zip
+*.firebase
+firebase-debug.log
diff --git a/client/.env.example b/client/.env.example
index b4d1c9a..bbbc086 100644
--- a/client/.env.example
+++ b/client/.env.example
@@ -2,4 +2,6 @@ NEXT_PUBLIC_BASE_PATH=''
NEXT_PUBLIC_FIREBASE_WEB_API_KEY=AIzaSyD65tGpVAl23Iu_IfLatCO3ikoNQzR347o
NEXT_PUBLIC_FIREBASE_WEB_AUTHDOMAIN=climate-profile-app.firebaseapp.com
NEXT_PUBLIC_FIREBASE_WEB_PROJECT_ID=climate-profile-app
-NEXT_PUBLIC_FIREBASE_WEB_STORAGE_BUCKET=climate-profile-app.appspot.com
\ No newline at end of file
+NEXT_PUBLIC_FIREBASE_WEB_STORAGE_BUCKET=climate-profile-app.appspot.com
+# Uncomment this line if using Docker Desktop and WSL2 on Windows OS
+# WATCHPACK_POLLING=true
\ No newline at end of file
diff --git a/client/Dockerfile b/client/Dockerfile
new file mode 100644
index 0000000..9217d1a
--- /dev/null
+++ b/client/Dockerfile
@@ -0,0 +1,29 @@
+FROM node:20.15.0-alpine as base
+RUN mkdir -p /opt/client
+WORKDIR /opt/client
+RUN adduser -S client
+RUN chown -R client /opt/client
+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/client/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/client/README.md b/client/README.md
index 38cf6ed..a033046 100644
--- a/client/README.md
+++ b/client/README.md
@@ -33,13 +33,14 @@ password: useruser
2. Set up the environment variables. Create a `.env`, `.env.local` and a `.env.development` files inside the root project directory with reference to the `.env.example` file.
- | Variable Name | Description |
- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+ | Variable Name | Description |
+ | --- | --- |
| NEXT_PUBLIC_BASE_PATH | Root directory path name that NextJS uses for assets, media and client-side routing for the app.
Set its value to blank `''` when working on development mode in localhost.
Set its value to the sub-directory name where the exported NextJS app is to be deployed, i.e. `/` when
deploying on a repository (sub-directory) of a root GitHub Pages site, i.e, on `https://.github.io/` |
- | NEXT_PUBLIC_FIREBASE_WEB_API_KEY | Firebase web API key from the Firebase Project Settings configuration file. |
- | NEXT_PUBLIC_FIREBASE_WEB_AUTHDOMAIN | Firebase web auth domain key from the Firebase Project Settings configuration file. |
- | NEXT_PUBLIC_FIREBASE_WEB_PROJECT_ID | Firebase web project ID from the Firebase Project Settings configuration file. |
- | NEXT_PUBLIC_FIREBASE_WEB_STORAGE_BUCKET | Firebase web storage bucket key from the Firebase Project Settings configuration file. |
+ | NEXT_PUBLIC_FIREBASE_WEB_API_KEY | Firebase web API key from the Firebase Project Settings configuration file. |
+ | NEXT_PUBLIC_FIREBASE_WEB_AUTHDOMAIN | Firebase web auth domain key from the Firebase Project Settings configuration |
+ | NEXT_PUBLIC_FIREBASE_WEB_PROJECT_ID | Firebase web project ID from the Firebase Project Settings configuration file. |
+ | NEXT_PUBLIC_FIREBASE_WEB_STORAGE_BUCKET | Firebase web storage bucket key from the Firebase Project Settings configuration file. |
+ | WATCHPACK_POLLING | Enables hot reload on NextJS apps (tested on NextJS v13.2.1) running inside Docker containers on a Windows host. Set it to `true` if running Docker Desktop with WSL2 on a Windows OS. |
3. Deploy the **Firestore Security Rules** defined in the `"firestore.rules"` file using the Firebase CLI.
`firebase deploy --only firestore:rules`
diff --git a/client/config/nginx/nginx.conf b/client/config/nginx/nginx.conf
new file mode 100644
index 0000000..1ffb382
--- /dev/null
+++ b/client/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 'webserver-kmz-prod'
+ # location /api {
+ # proxy_pass http://climate-server-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/client/config/nginx/nginx.full.conf b/client/config/nginx/nginx.full.conf
new file mode 100644
index 0000000..132efa6
--- /dev/null
+++ b/client/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/docker-compose.dev.yml b/docker-compose.dev.yml
new file mode 100644
index 0000000..4b88760
--- /dev/null
+++ b/docker-compose.dev.yml
@@ -0,0 +1,42 @@
+services:
+ # NextJS v13 app running on development mode
+ climate-client-dev:
+ container_name: climate-client-dev
+ image: weaponsforge/climate-client:dev
+ env_file:
+ - ./client/.env
+ build:
+ context: ./client
+ dockerfile: Dockerfile
+ target: development
+ networks:
+ - climate-dev
+ volumes:
+ - ./client:/opt/client
+ - /opt/client/node_modules
+ - /opt/client/.next
+ ports:
+ - "3000:3000"
+
+ # Express server for hosting KMZ files running in development mode
+ climate-server-dev:
+ container_name: climate-server-dev
+ image: weaponsforge/climate-server:dev
+ env_file:
+ - ./server/.env
+ build:
+ context: ./server
+ dockerfile: Dockerfile
+ target: development
+ networks:
+ - climate-dev
+ volumes:
+ - ./server:/opt/server
+ - /opt/server/node_modules
+ stdin_open: true
+ tty: true
+
+networks:
+ climate-dev:
+ name: climate-dev
+ external: false
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
new file mode 100644
index 0000000..4051ac6
--- /dev/null
+++ b/docker-compose.prod.yml
@@ -0,0 +1,19 @@
+services:
+ # NextJS exported app running on an nginx webserver
+ climate-client-prod:
+ container_name: climate-client-prod
+ image: weaponsforge/climate-client:latest
+ restart: always
+ build:
+ context: ./client
+ dockerfile: Dockerfile
+ target: production
+ networks:
+ - climate-prod
+ ports:
+ - "3000:3000"
+
+networks:
+ climate-prod:
+ name: climate-prod
+ external: false
diff --git a/scripts/docker-cleanup.sh b/scripts/docker-cleanup.sh
new file mode 100644
index 0000000..f66b53b
--- /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
diff --git a/server/.dockerignore b/server/.dockerignore
new file mode 100644
index 0000000..6dc8486
--- /dev/null
+++ b/server/.dockerignore
@@ -0,0 +1,8 @@
+.git
+.gitignore
+node_modules
+npm-debug.log
+Dockerfile
+.dockerignore
+.env
+*.zip
diff --git a/server/.env.example b/server/.env.example
index a88707e..5b0bac8 100644
--- a/server/.env.example
+++ b/server/.env.example
@@ -1,5 +1,6 @@
-ALLOW_CORS=1
-ALLOWED_ORIGINS=http://localhost:3000
FIREBASE_SERVICE_ACC=
FIREBASE_PRIVATE_KEY=
-AUTH_UID=
\ No newline at end of file
+AUTH_UID=
+# Uncomment these 2 CHOKIDAR lines if running Express on Docker Desktop and WSL2 on Windows OS
+# CHOKIDAR_USEPOLLING=true
+# CHOKIDAR_INTERVAL=1000
diff --git a/server/Dockerfile b/server/Dockerfile
new file mode 100644
index 0000000..d37afb4
--- /dev/null
+++ b/server/Dockerfile
@@ -0,0 +1,14 @@
+# BASE PROFILE
+FROM node:20.15.0-alpine as base
+RUN mkdir -p /opt/server
+WORKDIR /opt/server
+RUN adduser -S server
+RUN chown -R server /opt/server
+COPY package*.json ./
+
+# DEVELOPMENT PROFILE
+FROM base as development
+RUN npm install && npm cache clean --force
+COPY . ./
+USER server
+CMD ["sh"]
diff --git a/server/README.md b/server/README.md
index ef9de52..a1a1b9a 100644
--- a/server/README.md
+++ b/server/README.md
@@ -31,10 +31,11 @@ The following dependecies are used for this project. Feel free to experiment usi
2. Set up the environment variables. Create a `.env` file inside the **/server** directory with reference to the `.env.example` file.
- | Variable Name | Description |
- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+ | Variable Name | Description |
+ | --- | --- |
| FIREBASE_SERVICE_ACC | The project's private key file contents, condensed into one line and minus all whitespace characters.
The service account JSON file is generated from the Firebase project's Project Settings page, on Project Settings -> Service accounts -> Generate new private key |
- | FIREBASE_PRIVATE_KEY | The private_key entry from the service account JSON file.
**NOTE:** Experiment wrapping this value in double-quotes on WINDOWS OS localhost. Some systems may or may not require the double-quotes (i.e., Ubuntu).
|
+ | FIREBASE_PRIVATE_KEY | The private_key entry from the service account JSON file.
**NOTE:** Experiment wrapping this value in double-quotes on WINDOWS OS localhost. Some systems may or may not require the double-quotes (i.e., Ubuntu).
|
+ | AUTH_UID | Firebase user ID to associate uploading the cards seed documents from `"npm run cards:webscrape"` |
3. Initialize the Firestore database.
- Create and initialize the Firestore database.