Skip to content

Commit 5f00921

Browse files
authored
Merge pull request #60 from ciatph/dev
v1.1.2
2 parents cd0950b + d7467cf commit 5f00921

File tree

19 files changed

+253
-50
lines changed

19 files changed

+253
-50
lines changed

.dockerignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.git
2+
.gitignore
3+
node_modules
4+
npm-debug.log
5+
Dockerfile
6+
.dockerignore
7+
.env
8+
/server/.env

.github/workflows/release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ jobs:
118118
cp client/.env.example client/.env
119119
cp server/.env.example server/.env
120120
- name: Set release version number
121-
run: sed -i -e "s/latest/${{ github.event.release.tag_name }}/g" docker-compose.prod.yml
121+
run: sed -i -e "s/latest/${{ github.event.release.tag_name }}/g" docker-compose.app.yml
122122
- name: Build Images
123-
run: docker-compose -f docker-compose.prod.yml build
123+
run: docker-compose -f docker-compose.app.yml build
124124
- name: Push Images to Docker Hub
125-
run: docker-compose -f docker-compose.prod.yml push
125+
run: docker-compose -f docker-compose.app.yml push

Dockerfile.app

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# BASE PROFILE
2+
FROM node:14.19.3-alpine AS base
3+
RUN adduser -S appuser
4+
5+
# BASE SERVER
6+
FROM base as base-server
7+
RUN mkdir -p /opt/server
8+
WORKDIR /opt/server
9+
RUN chown -R appuser /opt/server
10+
COPY ./server/package*.json ./
11+
12+
# BASE CLIENT
13+
FROM base as base-client
14+
RUN mkdir -p /opt/client
15+
WORKDIR /opt/client
16+
RUN chown -R appuser /opt/client
17+
COPY ./client/package*.json ./
18+
19+
# BUILD CLIENT WEBSITE TARGET
20+
FROM base-client as builder-client
21+
RUN npm install
22+
COPY ./client ./
23+
RUN npm run build
24+
25+
# BUILD API DOCUMENTATION
26+
FROM base-server as builder-server
27+
RUN npm install && npm cache clean --force
28+
COPY ./server .
29+
RUN npm run build
30+
31+
# PRODUCTION CLIENT+SERVER PROFILE TARGET
32+
# Express backend running on pm2
33+
# Create React App (CRA) served from backend's static directory (using express.static() middleware)
34+
# Requirements: Disable CORS to allow Same Origin headers on client and server APIs
35+
FROM base-server as production
36+
ENV NODE_ENV production
37+
RUN npm ci --only=production && npm cache clean --force
38+
RUN npm install [email protected] -g
39+
COPY ./server .
40+
COPY --from=builder-server /opt/server/public/docs ./public/docs
41+
COPY --from=builder-client /opt/client/build ./public
42+
USER appuser
43+
EXPOSE 3001
44+
CMD ["pm2-runtime", "process.json"]

README.md

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ A basic web app client in the **/client** directory will show basic API usage an
1111
- [Usage](#usage)
1212
- [Available Scripts - server](#available-scripts---server)
1313
- [Installation and Usage Using Docker](#installation-and-usage-using-docker)
14+
- [Docker Dependencies](#docker-dependencies)
1415
- [Docker for Localhost Development](#docker-for-localhost-development)
1516
- [Docker for Production Deployment](#docker-for-production-deployment)
16-
- [Pre-built Server Docker Image](#pre-built-server-docker-image)
17+
- [Pre-built Server Docker Image](#pre-built-server-docker-image)
1718
- [References](#references)
1819

1920
## Requirements
@@ -50,9 +51,10 @@ A basic web app client in the **/client** directory will show basic API usage an
5051
| --- | --- |
5152
|FIREBASE_SERVICE_ACC| The project's private key file contents, condensed into one line and minus all whitespace characters.<br><br>The service account JSON file is generated from the Firebase project's **Project Settings** page, on **Project Settings** -> **Service accounts** -> **Generate new private key**|
5253
|FIREBASE_PRIVATE_KEY| The `private_key` entry from the service account JSON file.<br> <blockquote> **NOTE:** Take note to make sure that the value starts and ends with a double-quote on WINDOWS OS localhost. Some systems may or may not require the double-quotes (i.e., Ubuntu running on heroku).</blockquote> |
53-
|ALLOWED_ORIGINS|IP/domain origins in comma-separated values that are allowed to access the API. Include `http://localhost:3000` by default to allow CORS access to the `/client` app.|
54+
|ALLOWED_ORIGINS|IP/domain origins in comma-separated values that are allowed to access the API if `ALLOW_CORS=1`. Include `http://localhost:3000` by default to allow CORS access to the `/client` app.|
5455
|EMAIL_WHITELIST| Comma-separated email addresses linked to Firebase Auth UserRecords that are not allowed to be deleted or updated (write-protected)<br><br>Default value is `[email protected]`|
55-
|ALLOW_CORS|Allow Cross-Origin Resource Sharing (CORS) on the API endpoints.<br><br>Default value is `1`. Setting to `0` will make all endpoints accept requests from all domains, including Postman.|
56+
|ALLOW_CORS|Allow Cross-Origin Resource Sharing (CORS) on the API endpoints.<br><br>Default value is `1`, allowing access to domains listed in `ALLOWED_ORIGINS`. Setting to `0` will make all endpoints accept requests from all domains, including Postman.|
57+
|ALLOW_AUTH|Restrict access to the `POST`, `PATCH` and `DELETE` API endpoints by allowing signed-in Firebase user Bearer Authorization (Firebase token) checking.<br><br>Retrieve the signed-in Firebase token by signing in a user using the Firebase Web JS SDK `signInWithEmailAndPassword()` method, then retrieve the latest token value using `getIdTokenResult()`.<br><br>Default value is `1`. Setting to `0` will disable Bearer Authorization checking on the listed API endpoints.|
5658

5759
### client
5860

@@ -140,6 +142,14 @@ Copies the built `/client` website from `/client/build` to the server's root dir
140142

141143
We can use Docker to run dockerized **client** and **server** apps for local development. The following methods require Docker and Docker compose correctly installed and set up on your development machine.
142144

145+
### Docker Dependencies
146+
147+
The following dependencies are used to build and run the image. Please feel feel free to use other versions as needed.
148+
149+
1. Ubuntu 20.04 (Host)
150+
2. Docker version 20.10.17, build 100c701
151+
3. Docker Compose version v2.6.0
152+
143153
### Docker for Localhost Development
144154

145155
1. Set-up the environment variables and firebase configuration file for the **/client** app.
@@ -164,51 +174,85 @@ We can use Docker to run dockerized **client** and **server** apps for local dev
164174

165175
### Docker for Production Deployment
166176

167-
The following docker-compose commands build small client and server images targeted for creating optimized dockerized apps running on self-managed production servers. Hot reload is not available when editing source codes from `/client/src` or `/server/src`.
177+
#### Option #1 - Client and Server as (2) Separate Images and Services
178+
179+
The following docker-compose commands build small `client` and `server` images targeted for creating optimized dockerized apps running on self-managed production servers. An **Nginx** service serves the frontend `client` on port `3000`. The `server`, running on a separate **Nodejs (pm2)** service, is also served by the client's Nginx service in a reverse proxy on port `3001`. Hot reload is not available when editing source codes from `/client/src` or `/server/src`.
168180

169181
1. Install and set up the required **client** and **server** environment variables as with the required variables on [**Docker for Localhost Development**](#docker-for-localhost-development).
170182
2. Build the client and server docker services for production deployment.
171183
- `docker-compose -f docker-compose-prod.yml build`
172-
3. At this point, we can opt to push the docker images to a docker registry of your choice. (Requires sign-in to the selected docker registry).
173-
- `docker-compose -f docker-compose-prod.yml push`
174-
4. Create and start the client and server containers.
175-
`docker-compose -f docker-compose-prod.yml up`
176-
5. Run a script in the container to create the default `[email protected]` account, if it does not yet exist in the Firestore database.
177-
`docker exec -it server-prod npm run seed`
178-
6. Launch the dockerized (prod) client app on
184+
3. Create and start the containers.
185+
- `docker-compose -f docker-compose-prod.yml up`
186+
4. Run a script in the container to create the default `[email protected]` account, if it does not yet exist in the Firestore database.
187+
- `docker exec -it server-prod npm run seed`
188+
5. Launch the dockerized (prod) client app on
189+
`http://localhost:3000`
190+
6. Launch the dockerized (prod) server app's API documentation on
191+
`http://localhost:3001/docs`
192+
7. Stop and remove containers, networks, images and volumes:
193+
- `docker-compose -f docker-compose-prod.yml down`
194+
195+
#### Option #2 - Client and Server Bundled in (1) Image and Service
196+
197+
The following docker-compose commands build a small `server` image targeted for creating an optimized dockerized Express app running on self-managed production servers. The frontend `client` is served in an a static directory using the Express static middleware.
198+
199+
1. Install and set up the required **client** and **server** environment variables as with the required variables on [**Docker for Localhost Development**](#docker-for-localhost-development).
200+
- > **INFO:** This build method requires CORS checking dissabled, since the client and server will run on the same port (3001).
201+
> - Disable CORS by setting `ALLOW_CORS=0` in the **.env** file to avoid `Same Origin` errors.
202+
2. Build the client and server docker services for production deployment.
203+
- `docker-compose -f docker-compose-app.yml build`
204+
3. Create and start the containers.
205+
- `docker-compose -f docker-compose-app.yml up`
206+
4. Run a script in the container to create the default `[email protected]` account, if it does not yet exist in the Firestore database.
207+
- `docker exec -it firebase-users-admin-app npm run seed`
208+
5. Launch the dockerized (prod) client + server app on
179209
`http://localhost:3000`
180-
7. Launch the dockerized (prod) server app's API documentation on
210+
6. Launch the dockerized (prod) client + server app API documentation on
181211
`http://localhost:3001/docs`
182-
8. Stop and remove containers, networks, images and volumes:
183-
`docker-compose -f docker-compose-prod.yml down`
212+
7. Stop and remove containers, networks, images and volumes:
213+
- `docker-compose -f docker-compose-app.yml down`
184214

185215
## Pre-built Server Docker Image
186216

187-
**firebase-users-admin**'s `server` component is available as a stand-alone docker image on Docker Hub with customizable environment variables (.env file).
217+
The `server` component of **firebase-users-admin** is available as a stand-alone docker image on Docker Hub with customizable environment variables (.env file).
188218

189-
1. Pull the (production) **/server** docker image from Docker Hub.
190-
`docker pull ciatphdev/firebase-users-admin-server:v1.1.1`
219+
The server also serves the pre-built `client` website from a static directory using the `express.static()` middleware, following the build instructions from [**Option #2 - Client and Server Bundled in (1) Image and Service**](#option-2---client-and-server-bundled-in-1-image-and-service).
220+
221+
### Steps
222+
223+
1. Pull the (production) **/server** [docker image](https://hub.docker.com/repository/docker/ciatphdev/firebase-users-admin-app) from Docker Hub.
224+
`docker pull ciatphdev/firebase-users-admin-app:v1.1.2`
191225
2. Create a `.env` file.
192226
- Read [**Installation - server #3**](#server) for more information.
193-
- Replace the variables accordingly in the `.env` file.
227+
- Replace the variables accordingly in the `.env` file. Set `ALLOW_CORS=0` to allow `Same Origin` requests. Read [**Option #2 - Client and Server Bundled in (1) Image and Service**](#option-2---client-and-server-bundled-in-1-image-and-service) for more information.
194228
```
195229
ALLOWED_ORIGINS=http://localhost,http://localhost:3000,http://mywebsite.com,http://yourwebsite.com
196230
FIREBASE_SERVICE_ACC=YOUR-FIREBASE-PROJ-SERVICE-ACCOUNT-JSON-CREDENTIALS-ONE-LINER-NO-SPACES
197231
FIREBASE_PRIVATE_KEY=PRIVATE-KEY-FROM-FIREBASE-SERVICE-ACCOUNT-JSON-WITH-DOUBLE-QUOTES
198232
199-
ALLOW_CORS=1
233+
ALLOW_CORS=0
234+
ALLOW_AUTH=1
200235
```
201236
3. Run the image.
202237
```
203238
docker run -it --rm \
204-
--env-file .env
239+
--env-file .env \
205240
-p 3001:3001 \
206-
ciatphdev/firebase-users-admin-server:v1.1.1
241+
ciatphdev/firebase-users-admin-app:v1.1.2
207242
```
208243
4. Run a script in the container to create the default `[email protected]` account, if it does not yet exist in the Firestore database.
209-
`docker exec -it server-prod npm run seed`
244+
`docker exec -it firebase-users-admin-app npm run seed`
210245
5. Launch the server API documentation on
211246
`http://localhost:3001/docs`
247+
6. Launch the client website on `http://localhost:3001`.
248+
- Login using the superadmin account create on step # 4.
249+
```
250+
251+
password: 123456789
252+
```
253+
- Test the API routes by creating new accounts, editing or deleting existing accounts.
254+
- The signed-in user's Firebase Auth token is available on the **Home** page (http://localhost:3001/)
255+
212256
213257
214258
## References

client/.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.git
22
.gitignore
33
node_modules
4+
npm-debug.log
45
Dockerfile
56
.dockerignore

client/nginx/nginx.full.conf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ server {
6262
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;
6363

6464
# Reverse proxy to the client website
65-
# Requires the client service running on http://localhost:3000 (from a container or manually installed on host)
65+
# Requires the client service running on http://<MACHINE_PRIVATE_IP>:3000 (from a container or manually installed on host)
6666
location / {
67-
proxy_pass http://localhost:3000;
67+
proxy_pass http://<MACHINE_PRIVATE_IP>:3000;
6868
proxy_set_header Host $host;
6969
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
7070
proxy_set_header X-Forwarded-Proto $scheme;
@@ -79,9 +79,9 @@ server {
7979
}
8080

8181
# Reverse proxy to the backend API server
82-
# Requires the backend service running on http://localhost:3001 (from a container or manually installed on host)
82+
# Requires the backend service running on http://<MACHINE_PRIVATE_IP>:3001 (from a container or manually installed on host)
8383
location /api {
84-
proxy_pass http://localhost:3001;
84+
proxy_pass http://<MACHINE_PRIVATE_IP>:3001;
8585
proxy_set_header Host $host;
8686
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
8787
proxy_set_header X-Forwarded-Proto $scheme;

client/src/.eslintrc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module.exports = {
2929
quotes: ['error', 'single'],
3030
semi: ['error', 'never'],
3131
'no-unused-vars': 'off',
32-
'no-undef': 'off',
33-
'no-console': 2
32+
'no-undef': 'off'
33+
// 'no-console': 2
3434
}
3535
}

client/src/containers/updateuser/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const defaultLoadingState = {
1313

1414
function UpdateUserContainer () {
1515
const location = useLocation()
16-
const [state, setState] = useState(location?.state || defaultState)
16+
const [state, setState] = useState(location.state ? { ...location.state, password: '' } : defaultState)
1717
const [loading, setLoading] = useState(defaultLoadingState)
1818

1919
const onInputChange = (e) => {

docker-compose.app.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Builds the server and client in (1) image:
2+
# - Express backend that restarts itself on errors running on pm2
3+
# - Create React App (CRA) client served from a static directory (using express.static() middleware)
4+
# Requirements: Disable CORS to allow Same Origin headers on client and server APIs
5+
version: "3"
6+
services:
7+
# Built Create React App (CRA) website running on nginx
8+
firebase-users-admin-app:
9+
container_name: firebase-users-admin-app
10+
image: ciatphdev/firebase-users-admin-app:latest
11+
restart: always
12+
env_file:
13+
- ./server/.env
14+
- ./client/.env
15+
build:
16+
context: .
17+
dockerfile: Dockerfile.app
18+
target: production
19+
networks:
20+
- firebase-users-admin-app
21+
ports:
22+
- "3001:3001"
23+
24+
networks:
25+
firebase-users-admin-app:
26+
name: firebase-users-admin-app
27+
external: false

docker-compose.dev.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ services:
1010
context: ./client
1111
dockerfile: Dockerfile
1212
target: development
13+
networks:
14+
- firebase-users-admin-dev
1315
volumes:
1416
- ./client/public:/opt/client/public
1517
- ./client/src:/opt/client/src
@@ -26,7 +28,14 @@ services:
2628
context: ./server
2729
dockerfile: Dockerfile
2830
target: development
31+
networks:
32+
- firebase-users-admin-dev
2933
volumes:
3034
- ./server/src:/opt/server/src
3135
ports:
3236
- "3001:3001"
37+
38+
networks:
39+
firebase-users-admin-dev:
40+
name: firebase-users-admin-dev
41+
external: false

0 commit comments

Comments
 (0)