顯示具有 nodejs 標籤的文章。 顯示所有文章
顯示具有 nodejs 標籤的文章。 顯示所有文章

2023-03-26

將 React app 和 Node.js 寫的 API 放在同一台伺服器

將 React app 和 Node.js 寫的 API 放在同一台伺服器

要前後端分離,同時要將 React app 和 API 放在同一台伺服器相同的 port,以避免誇來源的問題。

此時可以用一台 Node express server 解決。

React app 發佈至 Express server 靜態資料夾

第一種方式是,把發佈後的 React 放在 build 資料夾(別的名稱也可以),並將 build 設定為靜態資料夾。

其餘後端的 API 可以定義在服務靜態 html 檔之前。

const express = require("express"); const fs = require("fs/promises"); const app = express(); app.use(express.static("build")); let html = ""; fs.readFile(__dirname + "/build/index.html").then((txt) => { html = txt.toString(); }); // app.use('/api', YOUR_ROUTER); // 可以把 api 掛在這裡 app.use("/api", (req, res) => { res.json({ name: "shinder", say: "hello" }); }); app.use((req, res) => { res.send(html); }); const port = 3005; app.listen(port, () => { console.log(`啟動 ${port}`); });

使用反向代理伺服器

另一種做法是使用反向代理伺服器,可以利用 http-proxy-middleware 套件,建立 proxy。

const express = require("express"); const { createProxyMiddleware } = require("http-proxy-middleware"); const app = express(); app.use(express.static("build")); // app.use('/api', YOUR_ROUTER); // 可以把 api 掛在這裡 app.use("/api", (req, res) => { res.json({ name: "shinder", say: "hello" }); }); app.use( "/", createProxyMiddleware({ target: "http://localhost:3000", changeOrigin: true, }) ); const port = 3005; app.listen(port, () => { console.log(`啟動 ${port}`); });

2020-11-27

Mailgun 資源

mailgun 資源

以 mailgun 使用程式發信時,首先要有自己的 domain,個人是使用 godaddy 的網域。 Mailgun 官網是以 Cloudflare 為範例說明,來說明如何設定網域的驗證:Domain-Verification-Walkthrough。 設定的 domain 以 mg.mydomain.com 為例。

申請 Sending API Keys

到 Mailgun 官網登入後,在左側欄點選「Sending」>「Domain」,選擇你要使用的 domain 後,選「Domain Settings」,再點選「Sending API Keys」建立所需的發信 key。

使用 Node.js 發信

官方有出 Node.js package mailgun-js,先在專案中安裝。 參考這篇官方的文件 How To Send Transactional Email In A NodeJS App Using The Mailgun API。 我的 .env 設定大概是長這樣:

MAILGUN_API_KEY=my_secret_key MAILGUN_FROM_WHO=service@mg.mydomain.com MAILGUN_DOMAIN=mg.mydomain.com

測試發給自己信箱時,有可能會進到垃圾郵件,如果沒看到郵件,先到垃圾郵件找找找不到的話,再進行除錯。

官方會提供一個 postmaster@mg.mydomain.com 的帳號,可以在 Domain settings > SMTP credentials 那邊看到。 重設密碼後,應該可以使用 nodemailer 發信。有些人以 nodemailer 搭配 nodemailer-mailgun-transport 使用。

2020-11-08

使用 docker-node 測試 nodejs 專案

使用 docker-node 測試 nodejs 專案

首先你的環境必須已經安裝 docker,參考「取得 docker」

# 取得 nodejs,在此使用較舊版本 10 docker pull node:10

在專案中建立 docker-compose.yml 檔:

version: "2" services: node: image: "node:10" working_dir: /Users/shinder/Dropbox/my-nodejs-proj environment: - NODE_ENV=development volumes: - ./:/Users/shinder/Dropbox/my-nodejs-proj ports: - 3000:3000 command: "node ./src/index.js"

其中,比較要注意的是 ports,第一個 3000 是 host 使用的 port,第二個 3000 是 container 使用的 port。 執行使用 up,關閉使用 down:

# 啟動 docker-compose up # 在背景中啟動 docker-compose up -d # 關閉 docker-compose down # 查看正在執行的 containers docker ps

*** 在啟動專案的時候出錯了「invalid ELF header」,無法載入 bcrypt 模組。 問題出在,bcrypt 是平台相依性的,我的 host 是 MacOS,container 則是 Debian。

目前想到的解法是,重新建立 image 檔,將專案內的檔案(排除 node_modules)複製到 container,然後再重新 npm install 所有套件。要設定三個檔案:.dockerignore,Dockerfile,docker-compose.yml。

# .dockerignore node_modules
# Dockerfile FROM node:10 RUN npm install -g nodemon RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY package.json /usr/src/app RUN npm install EXPOSE 3000
# docker-compose.yml version: "3" services: node: build: . working_dir: /usr/src/app environment: - NODE_ENV=development volumes: - ./:/usr/src/app - /usr/src/app/node_modules # 上式可以用 container 的路徑覆蓋原來 host 的 node_modules 目錄 ports: - 3000:3000 command: "npm run dev"

執行 docker-compose up 即可建立,若調整設定檔後要重建可使用 docker-compose build。 其它常用的指令:

# 連入 container 的 bash docker exec -it <container-id> /bin/bash # 列出所有 image 檔 docker image ls # 移除某個 image 檔 docker rmi <image-id>

2020-11-01

nginx + nodejs 使用 certbot

nginx + nodejs 使用 certbot

以下的設定參考 教學-申請Let’s Encrypt憑證與啟用https

承上篇,在 nginx 設定中加入一個 server /etc/nginx/sites-enabled/www.shinder.cc,並設定為:

server { listen 80; server_name www.shinder.cc shinder.cc; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Host $host; } }

啟動 node/express server,使用 port 3000。 然後測試 http://www.shinder.cc 是否可以看到 node/express 的執行內容。 安裝 certbot:

# 先加入 certbot 的 repo 資料 sudo add-apt-repository ppa:certbot/certbot # 更新 repo 並安裝 sudo apt update sudo apt install certbot

申請憑證:

sudo certbot certonly --webroot --webroot-path=/home/ubuntu/<express專案>/public/ -d www.shinder.cc -d shinder.cc

接著會有幾個詢問, email,是否同意授權,email是否要接收訊息等等。最後就是 key 所放的位置:

/etc/letsencrypt/live/www.shinder.cc/fullchain.pem /etc/letsencrypt/live/www.shinder.cc/privkey.pem

為了增加安全性產生一個2048-bit Diffie-Hellman的密碼組合:產生的 /etc/ssl/certs/dhparam.pem 之後會用到:

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

更改 nginx 設定

建立設定檔 /etc/nginx/snippets/ssl-www.shinder.cc.conf

ssl_certificate /etc/letsencrypt/live/www.shinder.cc/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.shinder.cc/privkey.pem;

建立另一個設定檔 /etc/nginx/snippets/ssl-params.conf

ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; ssl_ecdh_curve secp384r1; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; ssl_dhparam /etc/ssl/certs/dhparam.pem;

最後修改 nginx 設定檔 /etc/nginx/sites-enabled/www.shinder.cc

server { listen 443 ssl http2; listen [::]:443 ssl http2; include snippets/ssl-www.shinder.cc.conf; include snippets/ssl-params.conf; server_name www.shinder.cc shinder.cc; client_max_body_size 1G; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Host $host; } } server { listen 80; server_name www.shinder.cc shinder.cc; return 301 https://$server_name$request_uri; }

nginx 重新載入設定就可以測試 https 了。

自動更新憑證

使用 crontab 排程:

# 進入編輯 sudo crontab –e

加入下行,每日早上七點更新:

10 7 * * * /usr/bin/certbot renew --quiet --renew-hook "/bin/systemctl reload nginx"

如果 web document root 變更了,可以在 /etc/letsencrypt/renewal/www.shinder.cc.conf 檔案內修改 webroot_map 。

2020-05-14

在 Raspbian 上安裝 Node.js

在 Raspbian 上安裝 Node.js

參考 Install Node.js and Npm on Raspberry Pi

先在 terminal 上查看 Raspberry Pi 的 CPU 型號:

uname -m

我的機器是 Pi 4 model B,CPU 是 ARMv7l。

Node.js 下載頁面 找到 Linux Binaries (ARM) 的版本,目前 LTS 12.16.3 版提供 ARMv7 和 ARMv8。若要找支援 ARMv6 就要找比較舊的版本。複製連結,並使用 wget 下載。

wget https://nodejs.org/dist/v12.16.3/node-v12.16.3-linux-armv7l.tar.xz

解壓縮檔案:

# tar -xvf 檔名.tar.xz tar -xvf ./node-v12.16.3-linux-armv7l.tar.xz

將檔案複製到 /usr/local/ 裡:

cd node-v12.16.3-linux-armv7l/ sudo cp -R * /usr/local/

最後查看版本,沒問題的話就完成安裝了:

node -v npm -v

這種安裝方式的缺點是,如果要移除 Node.js,由於沒有記錄複製的檔案,會無法移除所有檔案。

另一種安裝方式是使用 NodeSource Distributions,這個工具是由 NodeSource 這家公司所提供。

目前支援 Debian OS 的版本中,對 Raspberry Pi 支援的 CPU 只有 ARMv7 和 ARMv8,舊的 ARMv6 就不適合使用 NodeSource 的方案。以下是安裝的步驟:

先更新系統套件:

sudo apt-get update sudo apt-get dist-upgrade

NodeSource Distributions 查看支援 Debian 的版本,以下是安裝 12.x 的範例:

# Using Debian, as root curl -sL https://deb.nodesource.com/setup_12.x | bash - apt-get install -y nodejs

最後查看 Node 版本確認即可。

2020-05-05

NodeJS 將 session 資料存入 MySQL

NodeJS 將 session 資料存入 MySQL

一般使用 express.js 時,使用的 session 套件為 express-session。使用記憶體存放 session 資料的做法:

const session = require('express-session'); app.use(session({ saveUninitialized: false, resave: false, secret: '你的 cookie 加密字串', cookie: { maxAge: 1200000 // 單位為毫秒 } }));

若要將 session 存入資料庫,需要先安裝 express-mysql-session 套件。設定方式如下,其中的 db_connect2.js 請看 上篇

const session = require('express-session'); const MysqlStore = require('express-mysql-session')(session); const db = require(__dirname + '/db_connect2'); const sessionStore = new MysqlStore({}, db); app.use(session({ saveUninitialized: false, resave: false, secret: '你的 cookie 加密字串', store: sessionStore, cookie: { maxAge: 1200000 } }));

若使用 session 可以在資料庫看到這樣的資料:

CREATE TABLE IF NOT EXISTS `sessions` ( `session_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `expires` int(11) unsigned NOT NULL, `data` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `sessions` (`session_id`, `expires`, `data`) VALUES ('8CDH6O91CkkY_1DpJs7h3YmzbqgQeqrF', 1588332706, '{"cookie":{"originalMaxAge":1200000,"expires":"2020-05-01T11:31:42.263Z","httpOnly":true,"path":"/"},"hello":"shinder"}'); ALTER TABLE `sessions` ADD PRIMARY KEY (`session_id`);

2020-05-04

NodeJS 使用 mysql2 連線 MySQL

NodeJS 使用 mysql2 連線 MySQL

Node 連線 MySQL 資料庫,常用的套件為 mysqlmysql2。mysql 是比較資深的套件,但缺點是沒有直接支援 Promise,所以在使用上若要使用 Promise 需要使用 bluebird 之類的套件。

mysql2 標榜更快,支援 Promise。以下為連線的 module ( db_connect2.js ):

const mysql = require('mysql2'); const pool = mysql.createPool({ host: 'localhost', user: 'root', password: 'root', database: 'test', waitForConnections: true, connectionLimit: 10, // 最大連線數 queueLimit: 0 }); module.exports = pool.promise(); // 滙出 promise pool

在 express.js 使用上的例子:

const db = require(__dirname + '/db_connect2'); app.get('/try-db', (req, res)=>{ const sql = "SELECT * FROM address_book LIMIT 3"; db.query(sql).then(([results, fields])=>{ res.json(results); }); });

2020-05-03

Babel-node: 在 Node 上使用全 ES6 語法

Babel-node: 在 Node 上使用全 ES6 語法

目前 Node.js 已經可以使用絕大部份的 ES6 語法來開發,其中不支援的語法主要是 import 和 export。 Node.js 原生只支援 CommonJS 的 require() 和 module.exports 的語法。 若要使用全 ES6 語法開發可以使用 Babel-Node。Babel-node CLI 和 Node CLI 功能一樣,但多了將 ES6 編譯成 ES5 的功能。

    1. 首先要先安裝三個 babel 套件:@babel/core, @babel/node, @babel/preset-env。
npm i @babel/core @babel/node @babel/preset-env
    1. 在專案中建立 babel.config.json
{ "presets": [ "@babel/preset-env" ] }
    1. 接著就可以使用 babel-node 執行 js 程式:
npx babel-node src/index.js

如果使用 nodemon 啟動 express.js 測試專案,可以設定 package.json 中的 scripts:

{ "scripts": { "start": "nodemon --exec babel-node src/index.js" } }

使用 babel-node 啟動感覺比直接使用 node 啟動要來得慢一點,這就看個人決定是否要使用 babel-node 了。

2020-05-02

NodeJS 連線 MongoDB

NodeJS 連線 MongoDB

假設我們有個現成的 node/express 專案,首先安裝 mongodb 套件:

npm i mongodb

官方 mongodb 套件說明 裡面有各版本的教學和 API 文件。 先撰寫可以建立連線並使用某個 DB 的模組,在此檔名為 mdb_connect.js,只要滙出 getDB 方法即可:

const MongoClient = require('mongodb').MongoClient; const url = 'mongodb://localhost:27017'; const dbName = 'test'; let _db; // 存放對應 DB 的物件 const client = new MongoClient(url, {useUnifiedTopology: true}); client.connect() .then(c => { _db = client.db(dbName); // c 同 client }) .catch(error=>{ console.log('Cannot connect the mongodb server!'); console.log(error); }); const getDB = ()=>{ if(!_db) throw new Error('No MongoDB connection!'); return _db; }; module.exports = getDB;

模組被滙入之後,就開始依設定連線,並將 Db 物件存放到 _db 變數內,呼叫 getDB() 即回傳 _db 所指的 Db 物件。 以下為在 express app 下使用的情況:

const getDB = require(__dirname + '/mdb_connect'); app.get('/try-mdb', (req, res)=>{ const mdb = getDB(); mdb.collection('books') .find({}) .toArray() // Cursor 的 toArray() .then((ar)=>{ res.json(ar); }) });

之前 關聯查詢 的用法,另外用了 AggregationCursor 的 forEach() 方法:

app.get('/try-mdb2', (req, res)=>{ const mdb = getDB(); const ar = []; mdb.collection('books') .aggregate([ { $lookup: { from: 'publishers', foreignField: '_id', localField: 'publisher_id', as: 'publisher' } } ]) .forEach(function(doc){ ar.push(doc) }).then(()=>{ res.json(ar); }) });

2019-02-09

正式環境使用 PM2 啟動 Node.js app

以往開發 Node.js app 時常使用 nodemon 在測試環境中啟動 app,其 watch 的功能,可以在檔案變更後自動重新啟動 app,對開發人員來說相當方便。

不過當 app 要發佈到遠端的虛擬機及設定比較複雜時,PM2 似乎是比較理想的工具。個人的一個專案需求是,production 會同時需要 http 及 https, development 則只需使用 http,而且 http port number 不同。

PM2 說明文件

以下是大致上的使用步驟:

1. 安裝 PM2 (使用 ubuntu 18.04)
  sudo npm i -g pm2

2. 在專案的資料夾內初始化建立 PM2 的設定檔 ecosystem.config.js
  pm2 init

3. 修改 ecosystem.config.js
    apps: [{
        name: 'der_linebot',
        script: 'app.js',
        output: './logs/output.log',
        error: './logs/error.log',
        // log: './logs/combined.log',

        // Options reference: https://pm2.io/doc/en/runtime/reference/ecosystem-file/
        args: 'one two',
        instances: 1,
        autorestart: true,
        watch: false,
        max_memory_restart: '1G',
        env: {
            NODE_ENV: 'development',
            HTTP_PORT: 3000,
            HTTPS_PORT: 3002
        },
        env_production: {
            NODE_ENV: 'production',
            HTTP_PORT: 80,
            HTTPS_PORT: 443
        }
    }],

主要是設定 logs 的輸出資料夾及檔名。另外就是開發和正式環境使用不同的埠號。

4. 設定 package.json 的 scripts
  "scripts": {
    "dev": "pm2 start",
    "pro": "sudo pm2 start --env production"
  },

5. 在正式環境時,已經使用 sudo 以 root 的權限啟動,但還是發生某些檔案讀取時發生權限不足的問題。又不想切換成為系統管理者(su root)。解法是完全停掉 pm2 後(sudo pm2 kill),將 ~/.pm2/ 裡的 .sock 檔變更所有者及所有者群組,例如:
  sudo chown shinder:shinder /home/shinder/.pm2/rpc.sock /home/shinder/.pm2/pub.sock




2019-02-04

使用 Certbot 和 LetsEncrypt 實現 Node/Express 的 HTTPS

基本上是參考這篇
Node + Express + LetsEncrypt : Generate a free SSL certificate and run an HTTPS server in 5 minutes or less

原則上是依他的說明操作,這邊是防痴呆記錄一下。

1. 先在 local 準備一個簡單的 node/express 專案,只要先安裝 express 即可,寫個 hello world。

2. 在遠端開一台 VM。我是直接在 digitalocean 開一台 ubuntu 18.4 VM,每月五塊美金的(之後有需要再擴充)。開完就可以取得主機的 IP。

3. 設定 domain name。我個人是使用 godaddy 的服務,設定對應的子網域。

4. 使用 ssh 登入主機,更新 apt 並安裝 certbot。

$ apt-get update
$ apt-get install certbot

5. 使用 cetbot 啟用手動設定

$ certbot certonly --manual

在過程中,會需要回答一些問題、輸入連絡的 email 和申請的 domain name。

6. 看到以下訊息時,請先暫停,別冒然按下 Enter。

Create a file containing just this data:
辨識用的一串編碼
And make it available on your web server at this URL:
http://你的網域/.well-known/acme-challenge/一串編碼

7. 編輯你的專案,實作上述的功能,然後使用 sftp 上傳到主機。

app.get('/.well-known/acme-challenge/一串編碼', (req, res)=>{
    res.send('辨識用的一串編碼');
});

8. 在主機,使用另一個 terminal 啟動 node server,port number 使用 80。然後才在最初的 terminal 按下 Enter。

9. 看到以下的訊息就表示成功了

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/你的網域/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/你的網域/privkey.pem
   Your cert will expire on 2019-05-04. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:
   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

10. 在 node/express 專案中設定:
const privateKey = fs.readFileSync('/etc/letsencrypt/live/你的網域/privkey.pem', 'utf8');
const certificate = fs.readFileSync('/etc/letsencrypt/live/你的網域/fullchain.pem', 'utf8');
const credentials = {
    key: privateKey,
    cert: certificate,
    ca: certificate
};

11. 啟動 sever 應該就可以看到 https 了。




2017-01-21

使用 N 更新 Node.js

N 是用升級 Node 的 npm 套件,安裝:

sudo npm install -g n


安裝 N 之後即可升級 Node.js 或者更換版本。下行是安裝最新版本:

sudo n latest


下行是安裝 6.9 LTS:

sudo n 6.9


2013-07-14

使用 pm2 做 Node 行程管理

用 Nodejs 寫了個小 server,但掛點機會太高,所以用 forever 做行程管理,但偶爾還是會有連不上的情況。

原本想找出問題出在哪(記得安裝 forever 時就出現過版本不支援的情況),結果看到這篇「告别node-forever,拥抱PM2」,索性就把 forever 換成 pm2 跑跑看,目前還算 OK,希望就這樣保持下去 :-D。

pm2 專案位於 https://github.com/Unitech/pm2

2013-07-08

Installing node.js on Fedora 17 using nvm

參考 https://github.com/creationix/nvm 的作法
先安裝編譯用的工具, 原則上只要 gcc-c++ 即可, 其它工具則是用原碼安裝或 git 安裝時需要
yum install gcc-c++ make openssl-devel
yum install git
執行 .sh
curl https://raw.github.com/creationix/nvm/master/install.sh | sh
完成,重開 console 就可以開始使用 nvm

另外,在 console 裡查看 process 狀態 ( nodejs 執行的 process )
# ps -aux
停止某 process
# kill #pid
常駐 nodejs 程式可以使用 forever
npm install forever -g #全域安裝
forever start $js_path #開始執行程式
forever list           #列出目前執行的程式訊息 (包含 log 檔路徑)
forever stop $id       #停止執行程式,$id 為 forever 對程式的編號,由 0 開始
forever restart $id    #重新執行程式
forever cleanlogs      #清除所有 log

FB 留言