/
Installation Guide - xMember

Installation Guide - xMember

I. Server Requirements:

xMember supports all platforms: Windows, MacOS, Linux.

xMember needs a VPS server with at least:

  • 2GB of RAM - recommend 4GB

  • 40GB of HDD

  • 1 CPU core - recommend 2 cores at least

  • 3 domains / sub domains

    • api.[your-domain] and point to server IP address

    • admin.[your-domain] and point to server IP address

    • [your-domain] and point to server IP address

II. Software Requirements:

Ensure all softwares above are running before setup source code

Testing

From command line / terminal please run these commands to check

1. NodeJS

$ node -v v16.16.2

2. MongoDB

$ mongo --version MongoDB shell version v4.0.7 git version: 1b82c812a9c0bbf6dc79d5400de9ea99e6ffa025 allocator: system modules: none build environment: distarch: x86_64 target_arch: x86_64

3. Redis server

$ redis-server --version Redis server v=5.0.4 sha=00000000:0 malloc=libc bits=64 build=d4ba11298acbb366

4. FFMPEG

$ ffmpeg -version fmpeg version 2.8.15-0ubuntu0.16.04.1 Copyright (c) 2000-2018 the FFmpeg developers built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.10) 20160609 configuration: --prefix=/usr --extra-version=0ubuntu0.16.04.1 --build-suffix=-ffmpeg --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --cc=cc --cxx=g++ --enable-gpl --enable-shared --disable-stripping --disable-decoder=libopenjpeg --disable-decoder=libschroedinger --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmodplug --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librtmp --enable-libschroedinger --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzvbi --enable-openal --enable-opengl --enable-x11grab --enable-libdc1394 --enable-libiec61883 --enable-libzmq --enable-frei0r --enable-libx264 --enable-libopencv libavutil 54. 31.100 / 54. 31.100 libavcodec 56. 60.100 / 56. 60.100 libavformat 56. 40.101 / 56. 40.101 libavdevice 56. 4.100 / 56. 4.100 libavfilter 5. 40.101 / 5. 40.101 libavresample 2. 1. 0 / 2. 1. 0 libswscale 3. 1.101 / 3. 1.101 libswresample 1. 2.101 / 1. 2.101 libpostproc 53. 3.100 / 53. 3.100

5. NGINX

$ nginx -v nginx version: nginx/1.10.3

6. Pm2

$ pm2 -v [PM2] Spawning PM2 daemon with pm2_home=/Users/xxx/.pm2 [PM2] PM2 Successfully daemonized 4.4.0

7. Yarn

$ yarn -v 1.16.0

III. Setup and Test it Locally

We provide source code with 4 folders, check below and use in the app accordingly

  • user web frontend (front office)

  • admin backend (back office), admin control panel

  • api api of the app

  • config-example

    • env product environment example config

    • nginx nginx config example

1. API

  • Ensure node v12.x, MongoDB, Redis server, FFMPEG and yarn are running

  • CD to your API folder

  • Run yarn to install nodeJS dependencies. Make sure your NODE_ENV is not production or run yarn install --production=false to install both dependencies and devDependencies

$ yarn yarn install v1.16.0 [1/4] 🔍 Resolving packages... [2/4] 🚚 Fetching packages... [3/4] 🔗 Linking dependencies...
  • Create new .env from config-example > env > api.env in the root dir

  • Content looks like below

# production or development NODE_ENV=production # http port HTTP_PORT=8080 # secret which is using to generate jwt, password, etc... You should change with your key TOKEN_SECRET=1213456-example.com # mongo connection string. check here https://docs.mongodb.com/manual/reference/connection-string/ MONGO_URI=mongodb://localhost/xmember # redis server config # Redis connection string. Pattern redis[s]://[[username][:password]@][host][:port][/db-number] # DB-number is a integer number REDIS_URI=redis://127.0.0.1:6379/0 # redis prefix for queue service QUEUE_PREFIX=xmember_queue # queue event service prefix QUEUE_EVENT_PREFIX=xmember_queue_event MAILER_CONCURRENCY=2 TEMPLATE_DIR=templates DOMAIN=example.com BASE_URL=https://api.example.com USER_URL=https://example.com EMAIL_VERIFIED_SUCCESS_URL=https://example.com/auth/email-verified-success
  • Check Migration section to seed default data

  • Run yarn start:dev to start development env

$ yarn dev :48 PM] Starting compilation in watch mode... [2:49:13 PM] Found 0 errors. Watching for file changes. [Nest] 60068 - 08/03/2020, 2:49:17 PM [NestFactory] Starting Nest application... [Nest] 60068 - 08/03/2020, 2:49:17 PM [InstanceLoader] QueueModule dependencies initialized +134ms [Nest] 60068 - 08/03/2020, 2:49:17 PM [InstanceLoader] RedisModule dependencies initialized +0ms [Nest] 60068 - 08/03/2020, 2:49:17 PM [InstanceLoader] HttpModule dependencies initialized +1ms [Nest] 60068 - 08/03/2020, 2:49:17 PM [InstanceLoader] QueueModule dependencies initialized +0ms [Nest] 60068 - 08/03/2020, 2:49:17 PM [InstanceLoader] ServeStaticModule dependencies initialized +4ms ....
  • Open browsers and run http://localhost:8080 it should show success message or Hello World!

2. User Frontend Web

  • Ensure nodejs and yarn are running

  • From user root directory run yarn to install nodeJS dependencies. Make sure your NODE_ENV is not production or run yarn install --production=false to install both dependencies and devDependencies

$ yarn yarn install v1.16.0 [1/4] 🔍 Resolving packages... [2/4] 🚚 Fetching packages... [3/4] 🔗 Linking dependencies...
  • Create .env file from config-example > env > user.env and edit information accordingly. Content looks like below

NODE_ENV=production PORT=8081 # endpoint will be executed in the server side only API_ENDPOINT=http://localhost:8080 NEXT_PUBLIC_API_ENDPOINT=https://api.example.com NEXT_PUBLIC_SOCKET_ENDPOINT=https://api.example.com
  • Run yarn run dev to start development env

$ yarn run dev yarn run v1.16.0 $ ts-node --project tsconfig.server.json server/index.ts Warning: Built-in CSS support is being disabled due to custom CSS configuration being detected. See here for more info: https://err.sh/next.js/built-in-css-disabled > Using external babel configuration
  • Then you can open browser http://localhost:8081 to see the web

3. Admin Web

  • Similar Frontend web, same steps.

  • CD to admin fodler

  • Create .env file from config-example > env > admin.env and edit information accordingly. Content looks like below

NODE_ENV=production PORT=8082 API_ENDPOINT=http://localhost:8080 NEXT_PUBLIC_API_ENDPOINT=https://api.example.com NEXT_PUBLIC_SITE_URL=https://example.com NEXT_PUBLIC_MAX_SIZE_IMAGE=5 NEXT_PUBLIC_MAX_SIZE_FILE=100 NEXT_PUBLIC_MAX_SIZE_TEASER=200 NEXT_PUBLIC_MAX_SIZE_VIDEO=2000
  • You should change default port with package.json file and change port number in start script "start": "next start -p 8082" then open http://localhost:8082 to see your admin app

IV. Setup Production Environment with NGINX

1. API

  • Make sure you have installed nodeJS dependencies by yarn command or run yarn install --production=false to install both dependencies and devDependencies

  • In API root directory, run yarn build

$ yarn build yarn run v1.16.0 $ rimraf dist $ nest build && yarn copy-template $ cp -r ./src/templates ./dist/templates ✨ Done in 22.62s
  • Testing by run command yarn start:prod or node dist/main.js

$ yarn start yarn run v1.16.0 $ node dist/main [Nest] 60205 - 08/03/2020, 2:56:11 PM [NestFactory] Starting Nest application... ....
  • Run pm2 start dist/main.js --name=api to run API under background

Or run pm2 start yarn --interpreter bash --name xmember-api -- start:prod to run application under port 8080

Note: If have error when starting with Pm2, please try to start without interpreter

pm2 start yarn --name xmember-api -- start:prod

2. Frontend Web

  • Make sure you have installed nodeJS dependencies by yarn command or run yarn install --production=false to install both dependencies and devDependencies

  • Run yarn build

$ yarn build yarn run v1.16.0 $ next build && tsc && tsc --project tsconfig.server.json Warning: Built-in CSS support is being disabled due to custom CSS configuration being detected. See here for more info: https://err.sh/next.js/built-in-css-disabled > Using external babel configuration > Location: "/Users/tuong/Projects/frontend-base-reactjs/.babelrc" Creating an optimized production build Compiled with warnings. chunk styles [mini-css-extract-plugin] Conflicting order between: * css ./node_modules/css-loader??ref--6-1!./node_modules/less-loader/dist/cjs.js??ref--6-2!./src/components/video/video.less * css ./node_modules/css-loader??ref--6-1!./node_modules/less-loader/dist/cjs.js??ref--6-2!./src/components/common/layout/page.less * css ./node_modules/css-loader??ref--6-1!./node_modules/less-loader/dist/cjs.js??ref--6-2!./src/components/common/base/loader.less ....
  • Testing by run yarn start it should show success message

$ yarn start yarn run v1.16.0 next start ready - started server on http://localhost:8081
  • Change port in package.json file at start script if needed "start": "next start -p 8081" with 8081 is port number

  • Run pm2 start yarn --name xmember -- start to run application

3. Admin Web

  • Similar Frontend web, same steps. Make sure all node dependencies are installed or run yarn install --production=false to install both dependencies and devDependencies

  • Run yarn build

  • Run export NODE_ENV=production

  • Change port in package.json file at start script if needed "start": "next start -p 8082" with 8082 is port number

  • Run pm2 start yarn --name xmember-admin -- start to run application

4. Setup NGINX

  • Api

    • Let say our api code is in /var/www/api.xmember.info folder and api is running under port 8080, add new file in /etc/nginx/sites-enabled/api.xmember.info and content as bellow

    • You have to change with our server name and source code path

server { listen 80 ; root /var/www/api.xmember.info/public; index index.html; server_name api.xmember.info; # reduce the data that needs to be sent over network -- for testing environment gzip on; # gzip_static on; gzip_min_length 10240; gzip_comp_level 1; gzip_vary on; gzip_disable msie6; gzip_proxied expired no-cache no-store private auth; gzip_types # text/html is always compressed by HttpGzipModule text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; # allow the server to close connection on non responding client, this will free up memory reset_timedout_connection on; # cache informations about FDs, frequently accessed files # can boost performance, but you need to test those values open_file_cache max=200000 inactive=300s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # depend on server, we can change 128k, 256k, 512k... sendfile_max_chunk 1m; client_max_body_size 5000M; # to boost I/O on HDD we can disable access logs access_log off; error_log off; location / { try_files $uri @app; } location @app { proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $http_host; proxy_pass http://localhost:8080; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; # WebSocket support proxy_connect_timeout 7d; proxy_send_timeout 7d; proxy_read_timeout 7d; } location /.well-known { alias /var/www/api.example.com/public/.well-known; } location /videos/protected/ { auth_request /authvideo; root /var/www/api.xmember.info/public/; } location = /authvideo { internal; set $query ''; if ($request_uri ~* "[^\?]+\?(.*)$") { set $query $1; } proxy_pass http://localhost:8080/user/assets/videos/auth/check?$query; proxy_pass_request_body off; proxy_set_header Content-Length ""; } location /photos/protected/ { auth_request /authphoto; root /var/www/api.xmember.info/public/; } location = /authphoto { internal; set $query ''; if ($request_uri ~* "[^\?]+\?(.*)$") { set $query $1; } proxy_pass http://localhost:8080/user/assets/photos/auth/check?$query; proxy_pass_request_body off; proxy_set_header Content-Length ""; } location /digital-products/protected { auth_request /authproduct; root /var/www/api.xmember.info/public/; } location = /authproduct { internal; set $query ''; if ($request_uri ~* "[^\?]+\?(.*)$") { set $query $1; } proxy_pass http://localhost:8080/user/assets/products/auth/check?$query; proxy_pass_request_body off; proxy_set_header Content-Length ""; } }
  • Frontend

    • Let say our frontend code is in /var/www/xmember.info folder and Frontend app is running under port 8081, add new file in /etc/nginx/sites-enabled/xmember.info and content as bellow

    • You have to change with our server name and source code path

server { listen 80; server_name xmember.info; root /var/www/xmember.info/public; # cache informations about FDs, frequently accessed files # can boost performance, but you need to test those values open_file_cache max=200000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; # to boost I/O on HDD we can disable access logs access_log off; error_log off; # copies data between one FD and other from within the kernel # faster than read() + write() sendfile on; # send headers in one piece, it is better than sending them one by one tcp_nopush on; # don't buffer data sent, good for small data bursts in real time tcp_nodelay on; # reduce the data that needs to be sent over network -- for testing environment gzip on; # gzip_static on; gzip_min_length 10240; gzip_comp_level 1; gzip_vary on; gzip_disable msie6; gzip_proxied expired no-cache no-store private auth; gzip_types # text/html is always compressed by HttpGzipModule text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; # allow the server to close connection on non responding client, this will free up memory reset_timedout_connection on; # request timed out -- default 60 client_body_timeout 10; # if client stop responding, free up memory -- default 60 send_timeout 2; # server will close connection after this time -- default 75 keepalive_timeout 30; # number of requests client can make over keep-alive -- for testing environment keepalive_requests 100000; types_hash_max_size 2048; sendfile_max_chunk 512k; client_max_body_size 200M; location / { try_files $uri @app; } location @app { proxy_pass http://localhost:8081; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Proxy ""; } location /.well-known { alias /var/www/api.example.com/public/.well-known; } location /_next/static/ { alias /var/www/xmember.info/.next/static/$1; access_log off; expires max; } location /static/ { alias /var/www/xmember.info/static/$1; expires max; autoindex off; } }
  • Admin

    • Let say our frontend code is in /var/www/admin.xmember.info folder and Frontend app is running under port 8082, add new file in /etc/nginx/sites-enabled/admin.xmember.info and content as bellow

    • You have to change with our server name and source code path

server { listen 80; server_name admin.xmember.info; root /var/www/admin.xmember.info/public; # cache informations about FDs, frequently accessed files # can boost performance, but you need to test those values open_file_cache max=200000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; # to boost I/O on HDD we can disable access logs access_log off; # copies data between one FD and other from within the kernel # faster than read() + write() sendfile on; # send headers in one piece, it is better than sending them one by one tcp_nopush on; # don't buffer data sent, good for small data bursts in real time tcp_nodelay on; # reduce the data that needs to be sent over network -- for testing environment gzip on; # gzip_static on; gzip_min_length 10240; gzip_comp_level 1; gzip_vary on; gzip_disable msie6; gzip_proxied expired no-cache no-store private auth; gzip_types # text/html is always compressed by HttpGzipModule text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; # allow the server to close connection on non responding client, this will free up memory reset_timedout_connection on; # request timed out -- default 60 client_body_timeout 10; # if client stop responding, free up memory -- default 60 send_timeout 2; # server will close connection after this time -- default 75 keepalive_timeout 30; # number of requests client can make over keep-alive -- for testing environment keepalive_requests 100000; types_hash_max_size 2048; sendfile_max_chunk 512k; client_max_body_size 200M; location / { try_files $uri @app; } location @app { proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $http_host; proxy_pass http://localhost:8082; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; } location /.well-known { alias /var/www/api.example.com/public/.well-known; } location /_next/static/ { alias /var/www/admin.xmember.info/.next/static/$1; access_log off; expires max; } location /static/ { alias /var/www/admin.xmember.info/static/$1; expires max; autoindex off; } }
  • Testing

    • Open browser and access: https://yourdomain.com to access web frontend

    • Open browser and access: https://admin.yourdomain.com to access web admin

5. Check applications are running under pm2

  • Run pm2 ls to see your applications

  • Run pm2 stop [id] to stop app or pm2 reload [id] to reload

  • Run applications in start up by pm2 startup then pm2 save

6. Set up NGINX with HTTPS

7. Migration

  • From API root directory run yarn migrate to seed default values for settings and users

    • Default admin: admin@[yourdomain] with domain is in .env file

    • Default password: adminadmin

    • If admin password is wrong, you can run node script reset-admin-pw to reset it to adminadmin

  • You can change default values in src > migrations folder

Related content