Navigate to project folder and run the following commands to install dependencies
$ npm install
$ cd client && npm install
And start development
$ npm run dev
To build and deploy
$ npm run build
$ npm start
package.json
keeps track of package dependenciespm2.config.js
provides environment variables forpm2
commandbuildspec.yml
is a configuration for building application on AWS CodePipeline whereartifacts
are the retained files and folders after build
The app was developed on Ubuntu 18.04 machine.
Download nodejs
and npm
commands.
Run
npx create-react-app <app-name> --template typescript
cd <app-name>
npm install
npm start
Increase inotify file watchers if you get the following error
Error: ENOSPC: System limit for number of file watchers reached
sudo sysctl fs.inotify.max_user_watches=524288
Note: the above command works until reboot
Packages
npm install --save react-router-dom mongodb express mongodb-client-encryption mongoose
npm install --save-dev @types/react-router-dom @types/express @types/mongodb @types/mongoose concurrently
- Component names must start with a capital letter to differentiate HTML tags from the component name.
class
toclassName
inside JSX- Use camel case, e.g.
background-color
tobackgroundColor
- To add custom attribute, use prefix
data-
, e.g.<div data-myAttr=3>
Add admin user using mongo
command interface
$ mongo
> use admin
> db.createUser(
{
user: "myUserAdmin",
pwd: passwordPrompt(), // or cleartext password below version 4.2
roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ]
}
)
Enable authentication and restart mongodb
service with systemctl restart mongodb
# /etc/mongodb.conf
auth=true
Authenticate using command line options
$ mongo --authenticationDatabase 'admin' -u 'adminuser' -p
Create restricted user
> use bookmarks
> db.createUser({
user: 'bookmarker',
pwd: 'password',
roles: [
{role: 'readWrite', db: 'bookmarks'}
]
})
Then authenticate using the new user
$ mongo --authenticationDatabase 'bookmarks' -u 'bookmarker' -p
Install express
for backend API calls. cors
is needed to make cross-origin requests work because during development React different port number than the backend.
mkdir bookmarkShare
cd bookmarkShare
npm install --save express cors nodemon mongoose
Note: --save
option adds package as a dependency on package.json
file. While --save-dev
for non-build environment, so these packages are not present in npm build
.
Create .gitignore
file
# .gitignore
node_modules
package-lock.json
For easier deployment either move React frontend to the subfolder, .e.g bookmarkShare/client
.
Install dotenv
package
npm install --save dotenv
Create .env
file at project root directory; this where we keep API keys.
# .env
MONGODB_URI='mongodb://user:password@localhost:27017/bookmarksdb'
Initially add .env
file without credentials to git, then ignore changes to it by adding it to .gitignore
and running the following command,
git add .env
git commit -m 'Added .env file'
git update-index --assume-unchanged .env
For continuous delivery, the better option is to create environment variable on the server side and use it in the application.
That can be done via Configuration under Environments on AWS Elastic Beanstalk. The environment variable is not visible to a user, so it can't be accessed via EC2 console because the environment variable is directly passed to the launching npm start
command.
We can either build static files locally and push to the git repo or let AWS build it for us. The latter allows to keep the repo clean and reduces its size.
If you already pushed build
files, then overwrite history with. The following command will delete client/build
from git history and local files as well
git filter-branch --tree-filter "rm -rf client/build" --prune-empty HEAD
You will need to force change to push remote
git push -f origin master
Based on logs, the default port is 8080 which gets routed to port 80 for public access
/var/log/web.stdout.log
----------------------------------------
Jun 3 19:37:43 ip-172-31-5-220 web: > [email protected] start /var/app/current
Jun 3 19:37:43 ip-172-31-5-220 web: > node app.js
Jun 3 19:37:43 ip-172-31-5-220 web: Server running at http://127.0.0.1:8080/
Create zip file using git for manual upload to Beanstalk. Make sure to not include root directory!
git archive --format=zip HEAD > app.zip
AWS Beanstalk automatically stars the application with npm start
command. If you need to apply changes that you made on the server, kill node
process and it will be automatically restarted,
sudo pkill -f node
The unzipped application is stored on /var/app/current/
.
To directly connect to AWS Beanstalk terminal, create key pair on Configuration under application environment. Then use the key to connect,
ssh -o 'IdentitiesOnly yes' -i ~/Downloads/key.pem [email protected]
We use -o
options so that only the specified key is used, otherwise AWS will drop multiple attemps to authenticate. Also use IP address to avoid DNS issues.
Note: When connecting to the EC2 instance via browser the user is can be root
, but for ssh connection it is ec2-user
.
Add github as a source for Codepipeline. When specifying buildspec leave the field empty so by default it looks for buildspec.yml
at the project root directory.
The install
commands are run before build
. The artifacts
indicates which files to save and deploy after build is finished. The artifacts files are placed under /var/app/current/
(older content is removed) on AWS EC2 server, and launched with npm start
.
# buildspec.yml
version: 0.2
phases:
install:
commands:
- echo Installing backend modules...
- npm ci --only=production
- echo Install frontend modules...
- cd client && npm ci
build:
commands:
- echo Building static files for client...
- npm run build
artifacts:
files:
- ./*
- node_modules/**/*
- client/build/**/*
Note: **/*
means to recursively select all files
If you get the following error, then change the version line to version: 0.2
on buildspec.yml
.
[Container] 2021/06/03 06:13:19 YAML location is /codebuild/output/src233444839/src/buildspec.yml
[Container] 2021/06/03 06:13:19 Phase complete: DOWNLOAD_SOURCE State: FAILED
[Container] 2021/06/03 06:13:19 Phase context status code: YAML_FILE_ERROR Message: invalid buildspec `version` specified: 1.0, see documentation
As an additional touch, change title of the website in public/index.html
and public/manifest.json
and replace favicon.ico
.
Using navigator.clipboard
is only available for HTTPS and localhost connections. If it is not available, we can make use of window.getSelection()
so that user themself can copy the selected text.
In order to fetch icon and page titles of webpage, its HTML page is searched for <title>
and <link rel="icon">
tags. This way our Express backend can respond with 3-tuple of bookmark, title and favicon URL texts.