好久不見

看了看記錄,確實是好久沒有更新了。。。看 VPS 的日志,居然有580多天沒有管過這個 VPS 了。趁著有時間,把 VPS 重裝了一遍。

近年來

這些年又發生了很多。。。人到中年,家里用洋垃圾折騰了個 NAS ,看了看機箱購買記錄,居然已經過去一年多了,淘寶店鋪都倒閉了。

很多東西現在也都布置在自己的 NAS 上了,回頭整理下使用過程中的記錄吧。

這次 VPS 重裝用了兩個晚上,搭配好了之前常用的東西,哈哈,寫這個也是測試下剛重新搭建的博客的更新的 hooks 是不是正常 - - ? ?

雖然有很多筆記的備份,感覺還是應該整理下。

新年好

回頭有空整理下近年來的筆記,考慮做成小教程,方便之后隨時查看。再記錄下近年來折騰 NAS 的記錄等。還有學過大半的 React ,由于不咋用,基本都忘記了。回頭把日常用到的東西還沒忘記的內容整理下吧,畢竟我的主業還是硬件開發,哈哈。

新的一年,說一句新年快樂!

本系列介紹:《全棧開發從入門到放棄》是本人學習如何使用JavaScript開發現代Web應用程序的過程記錄。課程的重點是使用ReactJS構建單頁面應用程序(SPA)。

本系列記錄了本人學習過程中的要點,不成體系。如果你想深入學習,建議尋找官方教程 ??


JavaScript標準的正式名稱是ECMAScript。由于瀏覽器不能支持所有JavaScript的最新特性,所以我們需要轉譯到一個更舊更兼容的版本。使用create-react-app創建的應用已配置為使用Babel自動轉譯。

Node.js是基于谷歌的 chrome V8 引擎的JavaScript運行時環境。代碼文件以 .js結尾,通過 node filename.js 命令運行文件。

變量(Variables)

在JavaScript中有以下幾種定義變量的方法:

const x = 1
let y = 5

console.log(x, y)
y += 10
console.log(x, y)
y = 'sometext'
console.log(x, y)
x = 4  // cause an error

const 實際是定義了一個常量, let定義了一個普通變量。推薦使用let而不是var。

數組(Arrays)

使用數組的示例如下:

const t = [1, -1, 3]
t.push(5)
console.log(t.length)
console.log(t[1])
t.forEach(value => {
  console.log(value)
})

即使使用const定義,也可以修改數組中的內容。因為數組是一個對象,數組變量指向這同一個對象。

遍歷元素的一種方法是使用 forEach ,如示例所示, forEach 接收一個函數作為入參。forEach 為數組中的每個元素調用這個函數,并將單個項作為參數傳遞。

使用push方法可以添加元素。但推薦使用函數式編程的技巧。函數編程范型的一個特點就是使用不可變的數據結構。在React代碼中,最好使用 concat 方法。它不向數組中添加元素,而是創建一個新數組,新數組中包含舊數組和新元素。

const t = [1, -1, 3]
const t2 = t.concat(5)
console.log(t2)

map方法可創建新數組。

const t = [1, 2, 3]
const m1 = t.map(value => value *2)
console.log(m1)

此外,還可轉換為不同的內容。

const m2 = t.map(value => '<li>' + value + '</li>')
console.log(m2)

還可使用解構賦值方式將數組中的單個元素賦值給變量。

const t = [1, 2, 3, 4, 5]
const [first, second, ...rest] = t
console.log(first, second)
console.log(rest)

例子中剩余的整數被“收集”到另一個數組中,分配給rest。

對象(Objects)

一個使用對象的例子:

const object1 = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
}

const object2 = {
  name: 'Full Stack web application development',
  level: 'intermediate studies',
  size: 5,
}

const object3 = {
  name: {
    first: 'Dan',
    last: 'Abramov',
  },
  grades: [2, 3, 5, 3],
  department: 'Stanford University',
}

// 對象元素的訪問可通過.或[]進行
console.log(object1.name)         // Arto Hellas is printed
const fieldName = 'age' 
console.log(object1[fieldName])    // 35 is printed

// 可以動態添加對象屬性
object1.address = 'Helsinki'
object1['secret number'] = 12341

函數(Functions)

函數定義的幾種方式:

const sum = (p1, p2) => {
  console.log(p1)
  console.log(p2)
  return p1 + p2
}
const result = sum(1, 5)
console.log(result)

// 只有一個參數時可省略括號
const square = p => {
  console.log(p)
  return p * p
}

// 只有一個表達式時可省略大括號
const square = p => p * p
// 該方法適合操作數組
const t = [1, 2, 3]
const tSquared = t.map(p => p * p)

// 或可使用function關鍵字定義
function product(a, b) {
  return a * b
}

const result = product(2, 6)

// 或使用函數表達式
const average = function(a, b) {
  return (a + b) / 2
}

const result = average(2, 5)

本課程推薦使用箭頭語法。

對象方法和”this”關鍵字

盡量避免使用this關鍵字以防止問題。 使用 setTimeOut方法,避免問題的方法:

const arto = {
  name: 'Arto Hellas',
  greet: function() {
    console.log('hello, my name is ' + this.name)
  },
}

setTimeout(arto.greet, 1000)
// 使用bind來保留原始的this
setTimeout(arto.greet.bind(arto), 1000)

類(Classes)

Javascript中的類非常像Java中的類,但其實質上仍然是Object。課程建議使用hooks特性,所以不具體使用類語法。

本系列介紹:《全棧開發從入門到放棄》是本人學習如何使用JavaScript開發現代Web應用程序的過程記錄。課程的重點是使用ReactJS構建單頁面應用程序(SPA)。

本系列記錄了本人學習過程中的要點,不成體系。如果你想深入學習,建議尋找官方教程 ??


新建React應用

創建一個React應用可用create-react-app,如:

npx create-react-app part1
cd part1
npm start

組件(component)

在使用React開發時,通常將需要渲染的內容定義為React組件。一個定義組件的示例如下:

const App = () => {
  const a = 10
  const b = 20
  return (
    <div>
      <p>Hello world</p>
      <p>
        {a} plus {b} is {a + b}
      </p>
    </div>
  )
}

JavaScript中大括號中的代碼會被計算,計算結果將嵌入到組件生成的HTML中??射秩緞討B內容。

JSX

JSX 是 JavaScript 的語法擴展。在底層,React 組件返回的 JSX 會被Babel編譯成 JavaScript 。

JSX 是類XML語言,每個標簽都需要關閉。如HTML中的<br>在JSX中,寫作<br />

多組件(Multiple component)

使用React編寫組件很容易,定義的組件可以在其他組件中使用。通常應用的組件樹頂部有個root組件叫App,但這也可修改。

props: 向組件傳遞數據

使用props,可將數據傳遞給組件??梢杂卸鄠€props值傳遞,如果props是JavaScript表達式,則需要使用花括號。

一些注意事項

  1. React組件名稱首字母必須大寫
  2. React組件內容通常需要包含一個根元素
  3. 如果不想在Dom樹中看到多余的div元素,可使用空元素包裝組件的返回內容。

本系列介紹:《全棧開發從入門到放棄》是本人學習如何使用JavaScript開發現代Web應用程序的過程記錄。課程的重點是使用ReactJS構建單頁面應用程序(SPA)。

本系列記錄了本人學習過程中的要點,不成體系。如果你想深入學習,建議尋找官方教程 ??


必要軟件安裝

  1. Chrome 或 Firefox 開發版。
  2. Git 和 GitHub。
  3. Visual Studio Code。
  4. Node.js

Web開發準則:始終打開開發者控制臺。

HTTP GET

服務器和瀏覽器使用HTTP協議相互通信, 瀏覽器可使用 Get 方法發送請求。

傳統的網絡應用

由于含有變量,模板字符串是可運行的字符串。

課程使用 Node.jsExpress 創建Web服務器。

使用 Console 選項卡和 console.log 命令可以方便調試。

事件處理和回調函數

調用事件處理程序的機制在 Javascript 中非常常見。事件處理函數被稱為回調函數。應用代碼時不直接調用函數本身,而是運行時瀏覽器會在事件發生的適當時間調用函數。

文檔對象模型(Document Object Model, DOM)

DOM 是一個 API, 它支持對web頁面對應的元素樹進行編程修改。

從console中操作文檔對象

html 文檔 DOM 樹的最頂層節點稱為文檔對象(document)??梢栽诳刂婆_的console中操作文檔對象。例如:

list = document.getElementsByTagName('ul')[0];
newElement = document.createElement('li');
newElement.textContent = 'Page manipulation from console is easy.是吧'
list.appendChild(newElement)

注意,上述更改只是更改瀏覽器頁面,并沒有將更改推送到服務器。

層疊樣式表(Cascading Style Sheets, CSS)

CSS 是一種用來確定web應用外觀的標記語言。

CSS 中的類選擇器用于選擇頁面中的某些部分,并對它們定義樣式規則。例如:

.container {
  padding: 10px;
  border: 1px solid;
}

類選擇器的定義始終以句點 . 開頭,并包含類的名稱。

CSS 屬性可以添加到 HTML 元素中,例如:

<body>
  <div class="container">
  <h1>Notes</h1>
  </div>
</body>

HTML 元素也可以有 class 以外的屬性。例如:

<div id="notes">

上述代碼中包含id屬性,JavaScript 代碼可使用 id 來查找元素。同樣可以在控制臺的Elements中更改元素樣式,這也不是永久性的。

表單與HTTP POST

表單 Form 具有屬性 action 和 method,例如:

<form action="/new_note" method="POST">
  <input type="text" name="note">
  <br>
  <input type="submit" value="Save">
</form>

上述屬性定義將表單作為一個 HTTP POST 請求提交到 /new_note 地址。數據作為 POST 請求的body發送到服務器。

例如,下述服務器端代碼可通過訪問請求對象req的body來訪問發送來的數據。

app.post('/new_note', (req, res) => {
  notes.push({
    content: req.body.note,
  });
  return res.redirect('/notes');
});

AJAX

異步的JavaScript和XML(Asynchronous JavaScript and XML, AJAX) 是2005年2月引入的一個術語。它描述了一種新的革命性的方法,這種方法使用包含在 HTML 中的 JavaScript 來獲取網頁內容,而且不需要重新渲染頁面?,F在已經普遍應用。

單頁面應用

單頁面應用 Single-page application (SPA) 只從服務器獲取一個HTML頁面,內容由JavaScript在瀏覽器中執行操作。

JavaScript庫

JavaScript工具庫操作頁面更容易。jQuery因為它所謂的跨瀏覽器兼容性發展開來。

由于SPA的發展,幾種更現代的開發方式流行開來。先是BackboneJS, 然后有AngularJS?,F在流行的有ReactVue.js。

此次學習課程中使用 React 和 Redux 庫。

全棧web開發

課程中將關注應用的所有部分: 從前端、后端到數據庫。將使用 JavaScript 編寫后端代碼,使用 Node.js 運行時環境。

0. 背景

LastPass 個人用戶手機端和電腦端同步要收費了。。。也一直種草 Bitwarden 很久了,想著反正有個服務器,不如自己整一個密碼管理服務。上Bitwarden官網一看,最小需求也比我這小破站大呀。。。

不過又看見有人用 Rust 重寫了一個服務端bitwraden_rs,用的服務器資源并不多。索性自己也整一個,記錄下過程。

1. 安裝docker

由于bitwarden_rs使用Docker鏡像安裝,參照Docker官方介紹 安裝docker。

1.1 卸載舊版本

其實不需要。。。。。

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

1.2 安裝docker

可自行選擇一種方式安裝,我選擇設置 repository 的方式。

  • 首先設置源。
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
  • 安裝
sudo yum install docker-ce docker-ce-cli containerd.io
  • 啟動
sudo systemctl start docker
  • 測試
sudo docker run hello-world
  • 添加用戶到 docker
sudo gpasswd -a king docker

2. 域名解析設置

依據自己的實際情況,設置域名解析。

如解析 https://bitwarden.robincn.com 到 server_ip。

3. 安裝bitwarden_rs

參照 bitwarden_rs官方介紹 安裝。

docker pull bitwardenrs/server:latest

4. 規劃轉發規則并設置nginx

根據我的小站情況,擬將所有訪問 https://bitwarden.robincn.com 的全部轉發至 8080 端口。docker 容器中運行 bitwarden_rs 服務并將容器內的80端口映射到主機的8080端口,容器內的3012端口映射到主機的3012端口。

4.1 ssl證書設置

和之前一樣使用 acme.sh 生成并安裝證書。

acme.sh --issue --dns dns_cf -d bitwarden.robincn.com --nginx
acme.sh --install-cert --nginx -d bitwarden.robincn.com --key-file /etc/nginx/bitwardenrobincn/key.pem --fullchain-file /etc/nginx/bitwardenrobincn/cert.pem --reloadcmd "service nginx force-reload"

4.2 nginx設置

修改 nginx 配置文件,將 bitwarden.robincn.com 的訪問全部轉發到主機的8080端口。

# vim /etc/nginx/nginx.conf
# http{ # 在http段內增加如下內容server

    upstream bitwardenrs-default { server localhost:8080;}
    upstream bitwardenrs-ws { server localhost:3012;}
	
	# 所有訪問都走https
    server {	
        listen 80;
        listen [::]:80;
        server_name bitwarden.robincn.com;
        return 301 https://$host$request_uri;
    }

	# 轉發對應端口到主機80803012
    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name bitwarden.robincn.com;

        ssl_certificate "/etc/nginx/passrobincn/cert.pem";
        ssl_certificate_key "/etc/nginx/passrobincn/key.pem";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers PROFILE=SYSTEM;
        ssl_prefer_server_ciphers on;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;

        proxy_intercept_errors on;  # for error page render
        large_client_header_buffers 2 2k;   # 解決 Android 客戶端同步問題

        # 解決網頁加載問題
        gzip off;
        proxy_max_temp_file_size 0;

        client_max_body_size 128M;

        location / {
                proxy_set_header Host  $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                proxy_pass http://bitwardenrs-default;
        }
        location /notifications/hub/negotiate {
                proxy_set_header Host  $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                proxy_pass http://bitwardenrs-default;
        }
        location /notifications/hub {
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header Connection $http_connection;

                proxy_pass http://bitwardenrs-ws;
        }

    }


5. 啟動 Bitwarden 并測試

5.1 啟動與測試

用命令行啟動 Bitwarden 并通過網頁添加用戶進行基礎測試。

注意本人將 Bitwarden 相關數據放在用戶目錄下。

docker run -d --name bitwarden \
-v /home/king/bitwarden/data/:/data/ \
-p 8080:80 \
-p 3012:3012 \
-e LOG_FILE=/data/bitwarden.log \
-e WEBSOCKET_ENABLED=true \
-e WEB_VAULT_ENABLED=true \
-e SIGNUPS_ALLOWED=true \
-e DOMAIN=https://bitwarden.robincn.com \
bitwardenrs/server:latest
# 返回docker的ID

字段說明如下

  • WEB_VAULT_ENABLED=true 開啟網頁端
  • SIGNUPS_ALLOWED=true 允許用戶注冊
  • 以上兩字段首次使用注冊用戶需要,之后可改為 false

如遇到問題,可使用以下命令查看相關日志。

docker ps
# 成功可看到STATUS中的 healthy ,確認PORTS映射關系無誤
docker logs *ID*  # 查看docker啟動日志
docker stop *ID*  # 停止docker實例
docker rm *ID*  # 刪除docker實例

5.2 新建用戶與導入數據

啟動成功后用瀏覽器訪問 https://bitwarden.robincn.com 即可新建用戶使用了。

參照官方說明導入數據。

6. 安裝Docker Compose并啟動服務

6.1 安裝

參照官方介紹安裝 Docker Compose.

sudo curl -L "https://github.com/docker/compose/releases/download/1.28.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

docker-compose --version

6.2 設置

data 目錄下新建 Docker Compress 服務描述文件。

# vim /home/king/bitwarden/docker-compose.yml
version: "3"

services:
        bitwarden:
                image: bitwardenrs/server
                container_name: bitwardenrs
                restart: always
                ports:
                        - "8080:80"
                        - "3012:3012"
                volumes:
                        - ./bw-data:/data
                environment:
                        LOG_FILE: "/data/bitwarden.log"
                        WEBSOCKET_ENABLED: "true"
                        SIGNUPS_ALLOWED: "false"  # 關閉用戶注冊 
                        WEB_VAULT_ENABLED: "false"  # 關閉瀏覽器訪問
                        DOMAIN: "https://bitwarden.robincn.com"

6.3 啟動服務

注意:首先關閉之前啟動的Bitwarden

docker stop *ID*
docker rm *ID*

使用 Docker Compose 啟動服務。

cd /home/king/bitwarden/
docker-compose up -d  # 啟動服務
docker-compose down		# 關閉服務
docker-compose restart  # 重啟服務

7. 安裝客戶端與其他設置

7.1 安裝客戶端

參照官網下載地址下載安裝各平臺的客戶端和/或插件。

7.2 其他設置

參照bitwarden_rs/wiki 進行其他設置(如fail2ban設置等)。