Building and deploying MyTunes to johnvincent.io
by John Vincent
Posted on August 15, 2019
MyTunes
MyTunes is a Folder Music Player.
MyTunes is the easiest way to play music stored on your local drive.
Use MyTunes to organize your music any way you prefer.
Live Deployment
Technical
MyTunes is built using the MERN stack. The front-end is built using React, Material-UI 4.0, Redux, HTML5, Sass and CSS3, the server-side using Node with Express as the web server.
MyTunes is fully responsive, adapting for mobile, table and desktop viewports.
All routing is handled in the front-end by React
An extensive API has been built to provide access to your music using Express, with many separate endpoints constructed.
MyTunes is fully unit tested on the front and server-side. For React testing, Jest has been used. For the server-side, Mocha and Chai, with extensive use of the Faker library to mock-out dependencies.
All client and server communications are performed using https.
MyTunes is deployed to an Ubuntu droplet at Digital Ocean and kept running using Pm2
MyTunes resources are served from Nginx Server with a reverse proxy to pass certain requests to a Node Express Server.
Technologies
Client
- React
- Material-UI
- Progressive Web App
- Redux
- Styled Components
- HTML5
- CSS3
- Sass
- Webpack
- Jest
- Enzyme
- Eslint
- Prettier
- Balsamiq
Server
Production Deployment
Website Updates
For extensive discussions regarding www.johnvincent.io
, please see Overview of johnvincent.io website
Update
Update the OS, please see Maintaining Ubuntu Droplet
Update SSL certificates
Enable HTTP
cd bin
./enable-http
Encrypt SSL certificates
./encrypt-ssl
Enable HTTPS
cd bin
./enable-https
Upgrade Node V6 to V8
Uninstall node
sudo apt-get remove nodejs
sudo apt-get remove npm
sudo apt-get update
sudo apt-get upgrade
Install Node V8
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
which node
/usr/bin/node
which npm
/usr/bin/npm
node -v
v8.16.0
npm -v
v6.4.1
Add Subdomain
Add subdomain, please see Configuring Google Domains
Add
Type: A
TTL: 1h
Data: 104.236.194.244
for each of
www.music
music
Verify subdomains
dig www.music.johnvincent.io
dig music.johnvincent.io
Configure HTTP Nginx
For details, please see Configure non-SSL Nginx
cd /var/www
sudo mkdir -p music/html/.well-known
Create index.html
sudo vi /var/www/music/html/index.html
<html>
<head>
<title>Welcome to music!</title>
</head>
<body>
<h1>Success! The server block is working!</h1>
</body>
</html>
Permissions
sudo chown -R jv:jv /var/www/music/html
cd /var/www/music/html
find . -type d -print0 | xargs -0 chmod 0755
find . -type f -print0 | xargs -0 chmod 0644
Server block
sudo vi /etc/nginx/sites-available/http/music
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name music.johnvincent.io www.music.johnvincent.io;
root /var/www/music/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ /.well-known {
allow all;
}
}
Enable Server Block
sudo ln -s /etc/nginx/sites-available/http/music /etc/nginx/sites-enabled/music
Restart Nginx
sudo nginx -t
sudo systemctl restart nginx
Test from browser
http://www.music.johnvincent.io
http://music.johnvincent.io
SSL Certificates
sudo letsencrypt certonly -a webroot --webroot-path=/var/www/music/html -d music.johnvincent.io -d www.music.johnvincent.io
Create
/etc/nginx/snippets/ssl-music-johnvincent.io.conf
ssl_certificate /etc/letsencrypt/live/music.johnvincent.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/music.johnvincent.io/privkey.pem;
Configure HTTPS Nginx
For details, please see Configure SSL Nginx
cd /etc/nginx/sites-available/http
sudo vi music
server {
listen 80;
listen [::]:80;
server_name music.johnvincent.io www.music.johnvincent.io;
return 301 https://www.music.johnvincent.io$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
include snippets/ssl-music-johnvincent.io.conf;
include snippets/ssl-params.conf;
server_name music.johnvincent.io;
return 301 https://www.music.johnvincent.io$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
include snippets/ssl-music-johnvincent.io.conf;
include snippets/ssl-params.conf;
include h5bp/basic.conf;
root /var/www/music/html;
index index.html;
server_name www.music.johnvincent.io;
location / {
try_files $uri /index.html;
}
location /api {
proxy_pass http://localhost:3001;
}
location = /analytics.js {
proxy_pass https://www.google-analytics.com;
expires 31536000s;
proxy_set_header Pragma "public";
proxy_set_header Cache-Control "max-age=31536000, public";
}
location /junk {
try_files $uri =503;
}
location ~* \.(svg|jpg|jpeg|png|gif|ico|css|js|pdf)$ {
add_header Cache-Control "max-age=31536000";
access_log off;
# expires 30d;
}
}
Note proxy_pass http://localhost:3001
This allows /api
to be routed to a Node server running on port 3001
Enable Https
cd bin
./enable-https
Test from Browser
http://www.music.johnvincent.io
http://music.johnvincent.io
https://www.music.johnvincent.io
https://music.johnvincent.io
All show the simple index.html
file that was created earlier.
Test SSL Certificates
Ensure all scores are A+
https://www.ssllabs.com/ssltest/analyze.html?d=music.johnvincent.io
https://www.ssllabs.com/ssltest/analyze.html?d=www.music.johnvincent.io
SSH to Github
For details, please see SSH to Github
Deployment Script
bin/deploy-music-app
#!/bin/sh
#
# script to get, build and deploy Music Player to nginx
#
# setup ssh to github
#
echo "setup ssh to github"
eval "$(ssh-agent)"
ssh-add -k ~/.ssh/id_github
#
cd
cd tmp
#
echo "Removing Logfile"
rm /home/jv/tmp/logfile.txt
#
CLONES_DIR="/home/jv/clones"
DOCROOT_DIR="/var/www/music/html"
SERVER_ROOT_DIR="/var/www/music/server"
SAVE_ENV_DIR="/home/jv/save-env"
#
echo "Removing clones directory $CLONES_DIR"
rm -rf $CLONES_DIR
#
echo "Creating clones directory $CLONES_DIR"
mkdir $CLONES_DIR
cd $CLONES_DIR
#
echo "Git clone desired repositories to $CLONES_DIR"
git clone git@github.com:johnvincentio/music-player $CLONES_DIR
#
# Make Music Player Client
#
# copy .env file
#
echo "Copy Music Player client .env file to $CLONES_DIR/client"
cp -r $SAVE_ENV_DIR/client.env $CLONES_DIR/client/.env
echo "Make the Music Player client"
cd $CLONES_DIR/client
echo "Npm install the Music Player client $CLONES_DIR/client"
npm install
#
echo "Make Music Player client production"
npm run production
#
echo "Minify $CLONES_DIR/client/dist/index.html"
cp dist/index.html dist/index.work
html-minifier dist/index.work --remove-comments --output dist/index.html
rm dist/index.work
#
# Make Music Player Server
#
# copy .env file
#
echo "Copy Music Player server .env file to $CLONES_DIR/server"
cp -r $SAVE_ENV_DIR/server.env $CLONES_DIR/server/.env
#
echo "Make the Music Player server"
cd $CLONES_DIR/server
#
echo "Npm install the Music Player server $CLONES_DIR/server"
npm install
#
#
# Delete files in nginx docroot
#
echo "Delete files in Nginx Docroot $DOCROOT_DIR"
rm -rf $DOCROOT_DIR/*
#
# Delete files in nginx server-root
#
echo "Delete files in Nginx server root $SERVER_ROOT_DIR"
rm -rf $SERVER_ROOT_DIR/*
#
# Copy client files to nginx
#
echo "Copy client files to $DOCROOT_DIR"
cp -r $CLONES_DIR/client/dist/* $DOCROOT_DIR
#
# set permissions
#
echo "Setting permissions on $DOCROOT_DIR"
sudo chown -R jv:jv $DOCROOT_DIR
sudo chmod 0755 $DOCROOT_DIR
find $DOCROOT_DIR -type d -print0 | xargs -0 chmod 0755 # For directories
find $DOCROOT_DIR -type f -print0 | xargs -0 chmod 0644 # For files
#
# Copy server files to nginx
#
echo "Copy server files to Nginx $SERVER_ROOT_DIR"
cp -r $CLONES_DIR/server/* $SERVER_ROOT_DIR
cp -r $CLONES_DIR/server/.env $SERVER_ROOT_DIR
#
# set permissions
#
echo "Setting permissions on $SERVER_ROOT_DIR"
sudo chown -R jv:jv $SERVER_ROOT_DIR
sudo chmod 0755 $SERVER_ROOT_DIR
find $SERVER_ROOT_DIR -type d -print0 | xargs -0 chmod 0755 # For directories
find $SERVER_ROOT_DIR -type f -print0 | xargs -0 chmod 0644 # For files
#
echo "Handle PM2"
cd $SERVER_ROOT_DIR
handle-pm2
#
echo "Restarting Nginx"
sudo nginx -t
sudo systemctl restart nginx
#
echo "Mongo Status"
sudo systemctl status mongodb
#
echo "Completed"
Create .env files
cd
cd save-env/music-player
Create client.env
#
# production
#
NODE_ENV=production
#
# HOME_URL - dev and prod are different
#
HOME_URL=https://www.music.johnvincent.io
SERVER_URL=https://www.music.johnvincent.io
Create server.env
#
PORT=3001
#
LOG_LEVEL=debug
LOG_ENV=prod
LOG_FILE=/home/jv/tmp/logfile.txt
#
CLIENT_SERVER=https://www.music.johnvincent.io
MUSIC_ROOT_DIRECTORY=/home/jv/music-folder/music
Deploy
cd
cd bin
./deploy-music-app
Make a Music Folder
Remote server
cd
mkdir music-folder
From mac, prepare music in tmp/music
Copy over SSH
cd
cd tmp
rsync -r --exclude=".git" --exclude=".gitignore" --exclude=".DS_Store" \
music mywebsite:music-folder
Test
https://www.music.johnvincent.io/
Local Development
/Users/jv/Desktop/MyDevelopment/github/projects/music-player
Start Client
Client Port: 8020
cd client
npm start
Start Server
Server Port: 9020
cd server
npm start
Local Application
http://localhost:8020
Create Local Production Version
Important files
make-player.sh
play-music-player.command
cd /Users/jv/Desktop/MyDevelopment/github/website/music-player
./make-player.sh
which builds the system in /Users/jv/tmp/music-player
and copies the production system to /Users/jv/Desktop/OtherTools/music-player
Use Mac Automator to create Music-Player.app
- Open Finder
- Applications
- Automator (or Automator.app)
- Application
- Choose (button)
- Run Shell Script (middle pane, big list of options)
- Shell: /bin/bash
- Pass input: as arguments
Enter the following
/Users/jv/Desktop/MyDevelopment/github/website/music-player/play-music-player.command
- File, Save:
- Save as: Music-Player
- Where: Applications
saves Music-Player.app in Applications as /Applications/Music-Player.app
Note that Music-Player.app
is the client and server parts of the application.
Run MyTunes
- Execute
Music-Player.app
to start the server.
To run the client
http://localhost:9000
or- select MyTunes from the new tab
Trouble
Usually, trouble is caused by port already in use.
See
/Users/jv/Desktop/OtherTools/music-player/server/error.log
Caching can also cause troubles. To disable cache:
- Google Chrome
- right click, Inspect
- Network
- Check: Disable cache
Digital Ocean
- Building and deploying MyTunes to johnvincent.io
- Building and deploying React Github Helper App to johnvincent.io
- Configuring Google Domains
- Create Ubuntu Droplet at Digital Ocean
- Create Ubuntu Droplet at Digital Ocean
- Deploy Feediator to Digital Ocean
- Deploy to Droplet
- Feediator Overview
- Feediator Project
- Maintaining Ubuntu Droplet
- Maintaining Ubuntu Droplet
- Overview of Deployment of a Node API Server
- Overview of johnvincent.io
- Overview of johnvincent.io Jekyll website
- Overview of React Gomoku
- Overview of React Hangman
- Overview of React Lights Out
- Overview of React Yahtzee
- TaskMuncher Overview
Johnvincent.io
- Backup Website from Digital Ocean
- Building and deploying MyTunes to johnvincent.io
- Building and deploying React Github Helper App to johnvincent.io
- Configure HTTP Nginx
- Configure HTTPS Nginx
- Configure PM2
- Configuring Google Domains
- Create SSL Certificates
- Create Ubuntu Droplet at Digital Ocean
- Deploy to Droplet
- Google Webmaster Tools
- Install Ubuntu Nginx
- Maintaining Ubuntu Droplet
- Overview of Deployment of a Node API Server
- Overview of johnvincent.io
- Overview of React Gomoku
- Overview of React Hangman
- Overview of React Lights Out
- Overview of React Yahtzee
- Restricting Access with HTTP Basic Authentication
- Update SSL Certificates
React
- Basic React
- Basic React Patterns
- Basic React Redux
- Basic React Redux App
- Basic React Testing with Jest and Enzyme
- Building and deploying MyTunes to johnvincent.io
- Building and deploying React Github Helper App to johnvincent.io
- Deploy React App to Heroku using Travis Continuous Integration
- Deploy TaskMuncher React App to AWS
- First time deploy TaskMuncher React App to Digital Ocean
- Gatsby and Client Only Components
- Gatsby Getting Started
- Gatsby React Icons
- Mac Visual Studio Code
- Material-UI
- Material-UI Pickers
- Material-UI Styling
- Optimizing TaskMuncher with Webpack 4
- Overview of React Gomoku
- Overview of React Hangman
- Overview of React Lights Out
- Overview of React Yahtzee
- React Material-UI
- React Production Issues
- React PropTypes
- React/Redux Node/Express Ecosystem
- Redux Dev Tools
- Responsive Material-UI
- Styling Material-UI components using Styled-Components
- TaskMuncher Performance
- Transforming Html with Webpack
- Update TaskMuncher for Lighthouse Findings
- Update TaskMuncher to be a Progressive Web App
- Update TaskMuncher to use React BrowserRouter
- Update TaskMuncher to Webpack v4, Babel v7, Material-UI v3
- Upgrading Babel and ESLint to use React Advanced Language Features
- Webpack Bundle Analyzer