在今天的教程中,我們將學習如何自行託管和設定我們的伺服器,這將使我們能夠在線上部署任何 Web 應用程式。有幾種在線部署應用程式的方法。兩種策略涉及使用 VPS、虛擬專用伺服器和共享託管平台(如 Vercel、Netlify、WordPress、GoDaddy 等)
VPS 是一種虛擬機,它在與其他使用者實體共享的伺服器上提供專用伺服器資源。它實際上是網站和應用程式的中間層託管,與共享託管相比,提供更多的控制和客製化。一些 VPS 託管平台的範例包括 Hetzner、Akamai、Vultr、Cloudcone。
另一方面,使用託管託管平台託管網站會更容易。此類平台提供用於建置和部署 Web 應用程式的工具、工作流程和基礎架構。這些平台自行執行負載平衡和快取。它們非常適合任何希望快速建置和部署 Web 應用程式而無需進一步配置的開發人員。
一如既往,使用每種策略都有優點和缺點。最顯著的區別之一是,使用 VPS 時,您可以獲得完全的自訂,因為您可以控制整個伺服器及其附帶的所有內容。這意味著設定開發環境、防火牆規則、託管等。這種定制增加了複雜性,並且您需要更多的技術支持,因為所有事情都是您自己做的。您應該知道託管平台對初學者非常友好,因為大多數工具都是預先設定的,並且您可以獲得支援和文件。因為它已經設定和管理,所以您將無法獲得從 VPS 獲得的高水準客製化。
此外,您還必須考慮價格差異。大多數 VPS 都是付費的,但您可以完全自訂您的伺服器,使其輕量級或強大到您需要的程度。在性能方面,這使其優於任何託管平台。後者確實有免費計劃,因此您需要查看差異。一般消費者會想要託管平台,因為它們是免費的。但是,如果您需要更多功能並希望在一個應用程式中託管高級應用程序,那麼 VPS 是您的最佳選擇。
我們的觀看清單追蹤器應用程式是一個簡單但功能強大的全端 CRUD 應用程序,將用於追蹤電影觀看清單。該應用程式還將使用戶能夠輕鬆添加他們想要觀看的電影或系列,更新電影標題或評級,以及刪除他們已經觀看或不再希望追蹤的電影。該應用程式為用戶提供了一個簡單的介面來組織和追蹤感興趣的電影,使其成為那些想要在關注清單上保持領先的電影愛好者的工具。
您可以看到該應用程式如下所示:
首頁
電影/系列商品頁面
新增項目頁面
在我們開始建立應用程式之前,必須設定並運行您的開發環境。確保您的電腦上安裝了以下軟體:
VS Code 有一個優秀的免費 SQLite Viewer 擴展,除了使用命令列之外,它也很有幫助。它有助於快速查看資料庫內的數據。
我們的 Watchlist Tracker 應用程式是使用非常現代且具有前瞻性的技術堆疊構建的,可提供出色的開發體驗。我選擇使用 Bun、Hono、Vite、TanStack、Hetzner 和 DeployHQ 等工具,因為它們都為開發人員提供了現代化的建置體驗。
讓我們來看看我們將在這個專案中使用的技術:
後端
前端
託管與部署
好吧,讓我們開始建立我們的應用程式!本節將分為兩部分:首先,我們將製作後端,然後我們將建立前端。
請在您的電腦上為名為 watchlist-tracker-app 的專案建立一個新資料夾,然後透過 cd 進入該資料夾。現在,使用此處顯示的以下命令為後端建立一個新的 Bun 專案:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
我們的專案現在應該已經建立了。我們只需安裝依賴項、進行設定並編寫一些伺服器程式碼。在程式碼編輯器中開啟項目。
現在使用以下命令安裝我們伺服器的依賴項:
bun add hono prisma @prisma/client
我們新增了 Bun 作為執行時間環境,Hono 作為我們的 API 伺服器,Prisma 作為我們的資料庫 ORM。
現在讓我們使用以下指令設定 Prisma ORM 和 SQLite:
npx prisma init
Prisma 現在應該配置為在我們的伺服器中工作,因此在下一步中,我們將配置我們的資料庫架構。因此,將 prisma/schema.prisma 中的所有程式碼替換為以下程式碼:
datasource db { provider = "sqlite" url = "file:./dev.db" } generator client { provider = "prisma-client-js" } model WatchlistItem { id Int @id @default(autoincrement()) name String image String rating Float description String releaseDate DateTime genre String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
我們的資料庫架構已設置,並且已連接到我們的 SQLite 資料庫檔案。
現在執行此遷移腳本來建立 SQLite 資料庫:
npx prisma migrate dev --name init
太好了,現在 Prisma 遷移已經完成,我們可以處理我們的 API 檔案了。
現在讓我們建立我們的主 API 文件,它將使用 Hono。前往 src/server.ts 內的伺服器檔案並將以下程式碼加入檔案:
import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { PrismaClient } from '@prisma/client'; const app = new Hono(); app.use(cors()); const prisma = new PrismaClient(); app.get('/watchlist', async (c) => { const items = await prisma.watchlistItem.findMany(); return c.json(items); }); app.get('/watchlist/:id', async (c) => { const id = c.req.param('id'); const item = await prisma.watchlistItem.findUnique({ where: { id: Number(id) }, }); return item ? c.json(item) : c.json({ error: 'Item not found' }, 404); }); app.post('/watchlist', async (c) => { const data = await c.req.json(); const newItem = await prisma.watchlistItem.create({ data }); return c.json(newItem); }); app.put('/watchlist/:id', async (c) => { const id = c.req.param('id'); const data = await c.req.json(); const updatedItem = await prisma.watchlistItem.update({ where: { id: Number(id) }, data, }); return c.json(updatedItem); }); app.delete('/watchlist/:id', async (c) => { const id = c.req.param('id'); await prisma.watchlistItem.delete({ where: { id: Number(id) } }); return c.json({ success: true }); }); Bun.serve({ fetch: app.fetch, port: 8000, }); console.log('Server is running on http://localhost:8000'); export default app;
透過此文件,我們的伺服器將使用 Bun 和 Hono 並在連接埠 8000 上運行。我們還擁有監視清單追蹤器的所有 CRUD(建立、讀取、更新、刪除)端點。我們所有的資料都保存在 SQLite 資料庫中。
剩下的就是為我們的伺服器建立運行和建置腳本。然後,我們可以測試端點以確保它們按預期工作。將這些運行腳本加入我們的 package.json 檔案:
"scripts": { "start": "bun run src/server.ts", "build": "bun build src/server.ts --outdir ./dist --target node" },
好的,現在如果您執行命令 Bun run start,您應該在終端機中看到以下內容,確認伺服器正在執行:
Server is running on http://localhost:8000
如果執行指令bun run build,它應該會建立一個可供生產使用的 dist 資料夾。當我們在 Hetzner 或任何線上伺服器上部署應用程式時,我們將需要這個。
好吧,讓我們快速測試我們的後端端點以確保它們按預期工作。然後,我們就可以開始開發前端了。我們有五個端點要測試:兩個 GET、一個 POST、一個 PUT 和一個 DELETE。我將使用 Postman 來測試 API。
監視清單應用 API POST 端點
方法:POST
端點:http://localhost:8000/watchlist
這是我們的 POST 端點,用於將包含電影/連續劇資料的 JSON 物件傳送到我們的資料庫。
監視清單應用 API 取得所有端點
方法:GET
端點:http://localhost:8000/watchlist
這是我們的主要 GET 端點,它將傳回前端將取得的物件陣列。如果我們尚未將任何資料發佈到資料庫,它會傳回一個包含我們資料的物件陣列或一個空數組。
Watchlist App API GET By ID Endpoint
方法:GET
端點:http://localhost:8000/watchlist/3
這是我們透過 ID 取得項目的 GET 端點。它僅傳回該對象,如果該項目不存在,則顯示錯誤。
監視清單應用 API PUT 端點
方法:PUT
端點:http://localhost:8000/watchlist/3
這是我們使用 ID 更新專案的 PUT 端點。它僅傳回該對象,如果該項目不存在,則顯示錯誤。
監視清單應用 API 刪除端點
方法:刪除
端點:http://localhost:8000/watchlist/3
這是我們的 DELETE 端點,用於使用 ID 刪除項目。它會傳回一個成功對象,如果該項目不存在則顯示錯誤。
也就是說,我們的 API 已啟動並正在運作。我們現在可以開始編寫前端程式碼了。
確保您位於watchlist-tracker-app 的根資料夾中,然後執行下面的這些腳本以使用Vite 建立一個React 項目,該項目是為TypeScript 設定的,並包含我們所有的套件和依賴項:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
這個腳本基本上是使用 Bun 執行環境來安裝和設定我們的專案。所有必需的文件和資料夾都已創建,因此我們只需添加程式碼即可。我們已經將 Vite 專案設定為使用 Tailwind CSS 進行樣式設置,並使用 TanStack Router 進行頁面路由,使用 axios 獲取數據,使用 dayjs 在表單中進行日期轉換。
感謝這個建置腳本,我們的工作現在明顯簡單了,所以讓我們開始將程式碼新增到我們的檔案中。首先是一些設定檔。將 tailwind.config.js 檔案中的所有程式碼替換為以下程式碼:
bun add hono prisma @prisma/client
這個文件非常有解釋性。我們需要此文件,以便 Tailwind CSS 在我們的專案中正常運作。
現在用此程式碼替換 src/index.css 檔案中的所有程式碼,我們需要新增 Tailwind 指令,以便我們可以在 CSS 檔案中使用它們:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
新增這些指令後,我們可以在所有 CSS 檔案中存取 Tailwind CSS 樣式。接下來刪除 App.css 檔案中的所有 CSS 程式碼,因為我們不再需要它。
好吧,現在是最終的配置文件,然後我們就可以處理我們的頁面和組件了。
將此程式碼新增至根資料夾中的 api.ts 檔案:
bun add hono prisma @prisma/client
此檔案匯出我們的前端需要在後端連接的端點。請記住,我們的後端 API 位於 http://localhost:8000。
好的,讓我們用這個新程式碼取代 App.tsx 檔案中的所有程式碼:
npx prisma init
這是我們應用程式的主要入口點元件,該元件保存我們所有頁面的路由。
接下來,我們將處理主要元件和頁面。我們有三個元件檔和三頁文件。從元件開始,將此程式碼新增至我們的檔案中的 elements/AddItemForm.tsx:
datasource db { provider = "sqlite" url = "file:./dev.db" } generator client { provider = "prisma-client-js" } model WatchlistItem { id Int @id @default(autoincrement()) name String image String rating Float description String releaseDate DateTime genre String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
該元件用於將項目新增至我們的資料庫。使用者將使用此表單元件向後端發送 POST 請求。
現在讓我們來加入 Components/FormField.tsx 的程式碼:
npx prisma migrate dev --name init
這是我們表單的可重複使用表單欄位元件。它使我們的程式碼保持乾燥,因為我們可以對多個欄位使用相同的元件,這意味著我們的程式碼庫更小。
最後讓我們加入 Components/Header.tsx 的程式碼:
import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { PrismaClient } from '@prisma/client'; const app = new Hono(); app.use(cors()); const prisma = new PrismaClient(); app.get('/watchlist', async (c) => { const items = await prisma.watchlistItem.findMany(); return c.json(items); }); app.get('/watchlist/:id', async (c) => { const id = c.req.param('id'); const item = await prisma.watchlistItem.findUnique({ where: { id: Number(id) }, }); return item ? c.json(item) : c.json({ error: 'Item not found' }, 404); }); app.post('/watchlist', async (c) => { const data = await c.req.json(); const newItem = await prisma.watchlistItem.create({ data }); return c.json(newItem); }); app.put('/watchlist/:id', async (c) => { const id = c.req.param('id'); const data = await c.req.json(); const updatedItem = await prisma.watchlistItem.update({ where: { id: Number(id) }, data, }); return c.json(updatedItem); }); app.delete('/watchlist/:id', async (c) => { const id = c.req.param('id'); await prisma.watchlistItem.delete({ where: { id: Number(id) } }); return c.json({ success: true }); }); Bun.serve({ fetch: app.fetch, port: 8000, }); console.log('Server is running on http://localhost:8000'); export default app;
由於這個標題元件,每個頁面都有一個帶有主導航的標題。
我們只剩下三頁了,然後我們的應用程式就完成了。因此,將以下程式碼加入pages/AddItem.tsx:
"scripts": { "start": "bun run src/server.ts", "build": "bun build src/server.ts --outdir ./dist --target node" },
這本質上是向我們的資料庫添加項目的頁面,其中包含表單元件。
是的,接下來讓我們加入pages/Home.tsx的程式碼:
Server is running on http://localhost:8000
正如您所想像的,這將是我們的主頁,它向後端發送 GET 請求,然後後端檢索資料庫中所有項目的物件數組。
最後加上pages/ItemDetail.tsx的程式碼:
bun create vite client --template react-ts cd client bunx tailwindcss init -p bun install -D tailwindcss postcss autoprefixer tailwindcss -p bun install @tanstack/react-router axios dayjs cd src mkdir components pages touch api.ts touch components/{AddItemForm,FormField,Header}.tsx pages/{AddItem,Home,ItemDetail}.tsx cd ..
此頁面按 ID 顯示各個項目頁面。還有一個用於編輯和刪除資料庫中的項目的表單。
就是這樣。我們的應用程式已準備好使用。確保後端伺服器和客戶端伺服器都在運作。您應該在瀏覽器中看到該應用程式正在運行:http://localhost:5173/.
在其資料夾中使用以下命令運行兩台伺服器:
module.exports = { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], theme: { extend: {}, }, plugins: [], };
好的,幹得好。我們的申請已完成!現在讓我們將其部署到 GitHub!
將我們的應用程式部署到 GitHub 非常簡單。首先將 .gitignore 檔案放入我們的 watchlist-tracker-app 的根資料夾中。您只需從後端或客戶端資料夾複製並貼上 .gitignore 檔案即可。現在前往您的 GitHub(如果沒有帳戶,請建立帳戶)並為您的 watchlist-tracker-app 建立儲存庫。
在 watchlist-tracker-app 的根資料夾內使用命令列上傳您的程式碼庫。請參閱此範例程式碼並將其改編為您自己的儲存庫:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
最後一點要注意。當我們將程式碼上傳到 Hetzner 時,我們將無法再透過 localhost 存取後端,因此我們必須更新 API 路由。有一個變數叫做 const API_URL = 'http://localhost:8000';在它們頂部的兩個檔案中。這些文件是 api.ts 和 Components/AddItemForm.tsx。將變數 API URL 替換為下面的 URL,然後將您的程式碼庫重新上傳到 GitHub。
bun add hono prisma @prisma/client
這就是全部了。您的程式碼庫現在應該在 GitHub 上在線,以便我們現在可以將其部署到 Hetzner。
我們的監視清單追蹤器應用程式完成後,現在可以將我們的應用程式部署到 Hetzner VPS 平台了。 Hetzner 是一個付費平台,但它提供的多功能性是無與倫比的。最便宜的計劃約為每月 4.51 歐元/4.88 美元/3.76 英鎊。擁有自架線上伺服器是非常值得的,因為作為開發人員,您可以使用它來學習、練習、生產部署等等。您可以隨時取消伺服器訂閱並在需要時取回。
大多數VPS本質上都是一樣的,因為它們都是可以運行不同作業系統的伺服器,例如Linux。每個 VPS 提供者都有不同的介面和設置,但基本結構完全相同。透過學習如何在 Hetzner 上自行託管應用程序,您可以輕鬆地重複使用這些相同的技能和知識,在不同的 VPS 平台上部署應用程式。
VPS 的一些用例包括:
您可以在這裡查看 Hetzner 的當前定價:
前往 Hetzner 網站,然後點擊頁面中間的紅色「註冊」按鈕。或者,您可以點擊右上角的登入按鈕。您應該會看到一個選單,其中包含 Cloud、Robot、konsoleH 和 DNS 選項。點擊其中任一個即可進入登入和註冊表單頁面。
現在,您應該看到登入和註冊表單頁面。點擊註冊按鈕建立帳戶並完成註冊程序。您可能需要使用 PayPal 帳戶或準備好護照來通過驗證階段。
您現在應該能夠登入您的帳戶並建立和購買伺服器。選擇您附近的位置,然後選擇 Ubuntu 作為映像。請參閱此範例以供參考。
在「類型」下,選擇共享 vCPU,然後選擇伺服器的配置。我們正在部署一個僅需要少量資源的簡單應用程式。如果您願意,可以隨意獲得更好的伺服器 - 這取決於您。專用 vCPU 效能較好,但成本較高。您也可以選擇 x86 (Intel/AMD) 和 Arm64 (Ampere) 處理器。
預設配置應該可以接受。請參閱下面的範例。不過,出於安全原因,新增 SSH 金鑰很重要。
SSH 金鑰提供了比傳統密碼更安全的伺服器驗證方式。金鑰需要採用 OpenSSH 格式,以確保伺服器的高度安全。根據您的作業系統,您可以在 Google 中搜尋「在 Mac 上產生 SSH 金鑰」或「在 Windows 上產生 SSH 金鑰」。我將為您提供在 Mac 上產生 SSH 金鑰的快速指南。
先開啟您的終端應用程序,然後鍵入以下命令,將「your_email@example.com」替換為您的實際電子郵件地址:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
-b 4096 部分確保金鑰為 4096 位,以提高安全性。接下來,在出現提示後儲存密鑰。您可以接受預設位置或選擇自訂位置:
bun add hono prisma @prisma/client
設定密碼是可選的,您可以跳過此步驟。現在,透過執行以下命令將 SSH 金鑰載入到 SSH 代理程式中:
首先,啟動代理:
npx prisma init
然後,新增 SSH 金鑰:
datasource db { provider = "sqlite" url = "file:./dev.db" } generator client { provider = "prisma-client-js" } model WatchlistItem { id Int @id @default(autoincrement()) name String image String rating Float description String releaseDate DateTime genre String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
要將 SSH 公鑰複製到剪貼簿,請執行:
npx prisma migrate dev --name init
這會複製公鑰,以便您可以將其新增至 Hetzner 或任何其他需要 SSH 金鑰的服務。將您的 SSH 金鑰貼到此表單框中並新增它。
您無需擔心磁碟區、置放群組、標籤或雲端配置,因為它們超出了本專案的範圍。備份可能會有所幫助,但會增加成本,因此對於該項目來說它們是可選的。我們稍後會做防火牆,所以你現在不用擔心。為您的伺服器選擇名稱,然後使用您目前的設定建立並購買伺服器,您的 Hetzner 帳戶就可以使用了。
好的,很好。我們現在在 Hetzner 上有一個帳戶。在下一節中,我們將設定防火牆、設定 Linux 作業系統並使應用程式上線。
在透過 SSH 進入 Linux 作業系統之前,我們先設定防火牆規則。我們需要打開連接埠 22 以便可以使用 SSH,並且需要打開連接埠 80,因為連接埠 80 是 TCP 端口,是使用 HTTP(超文本傳輸協定)的 Web 伺服器的預設網路連接埠。 它用於在 Web 瀏覽器和伺服器之間進行通信,傳送和接收 Web 內容。這就是我們如何讓我們的應用程式在線上運行。
導覽至主選單中的防火牆,然後使用如下所示的入站規則建立防火牆:
現在,隨著我們的防火牆工作,我們可以設定 Linux 環境並部署我們的應用程序,所以接下來我們就這樣做。
連接到我們的遠端 Hetzner 伺服器應該非常簡單,因為我們已經建立了用於登入的 SSH 金鑰。我們可以使用終端機甚至像 VS Code 這樣的程式碼編輯器進行連接,這將使我們能夠查看和瀏覽我們的無需使用命令列即可更輕鬆地存取文件。如果您想使用 Visual Studio Code Remote - SSH 擴展,則可以這樣做。對於這個項目,我們將堅持使用終端。
開啟終端機並輸入此 SSH 命令以登入遠端伺服器。將位址 11.11.111.111 替換為您的實際 Hetzner IP 位址,您可以在伺服器部分找到該位址:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
您現在應該登入您的伺服器,該伺服器顯示歡迎畫面和其他私人訊息,例如您的 IP 位址。我只是在這裡顯示螢幕的歡迎部分:
bun add hono prisma @prisma/client
好的,太好了。現在,我們終於可以開始運行一些命令,這些命令將在我們的應用程式上線之前設定我們的開發環境。順便說一句,如果您想退出並關閉與伺服器的 SSH 連接,您可以鍵入 exit 並單擊 Enter 按鈕,也可以使用鍵盤快捷鍵 Ctrl D。
我們需要做的第一件事是更新和升級 Linux 系統上的軟體包。我們可以使用一個命令來完成此操作,因此請在終端機中輸入此命令並按 Enter 鍵:
npx prisma init
現在,我們需要安裝其餘的開發套件和相依性。首先是 Node.js 和 npm,因此使用以下命令安裝它們:
datasource db { provider = "sqlite" url = "file:./dev.db" } generator client { provider = "prisma-client-js" } model WatchlistItem { id Int @id @default(autoincrement()) name String image String rating Float description String releaseDate DateTime genre String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
接下來是 Nginx,我們需要將其用作反向代理。反向代理是位於客戶端和後端伺服器之間的伺服器,將客戶端請求轉發到適當的後端伺服器,然後將伺服器的回應傳回給客戶端。它充當中介,管理和路由傳入請求以提高效能、安全性和可擴展性。因此使用以下命令安裝它:
npx prisma migrate dev --name init
我們將需要 Git,為此我們需要從 GitHub 提取程式碼並將其上傳到我們的遠端伺服器。因此使用以下命令安裝它:
import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { PrismaClient } from '@prisma/client'; const app = new Hono(); app.use(cors()); const prisma = new PrismaClient(); app.get('/watchlist', async (c) => { const items = await prisma.watchlistItem.findMany(); return c.json(items); }); app.get('/watchlist/:id', async (c) => { const id = c.req.param('id'); const item = await prisma.watchlistItem.findUnique({ where: { id: Number(id) }, }); return item ? c.json(item) : c.json({ error: 'Item not found' }, 404); }); app.post('/watchlist', async (c) => { const data = await c.req.json(); const newItem = await prisma.watchlistItem.create({ data }); return c.json(newItem); }); app.put('/watchlist/:id', async (c) => { const id = c.req.param('id'); const data = await c.req.json(); const updatedItem = await prisma.watchlistItem.update({ where: { id: Number(id) }, data, }); return c.json(updatedItem); }); app.delete('/watchlist/:id', async (c) => { const id = c.req.param('id'); await prisma.watchlistItem.delete({ where: { id: Number(id) } }); return c.json({ success: true }); }); Bun.serve({ fetch: app.fetch, port: 8000, }); console.log('Server is running on http://localhost:8000'); export default app;
Bun 運行時將有助於運行我們的應用程式。首先,在安裝Bun之前,我們必須按照要求安裝解壓縮包。之後我們就可以安裝Bun了。這些是我們需要的命令:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
bun add hono prisma @prisma/client
要在同一台 Hetzner 伺服器上執行 React 前端和後端伺服器,我們需要同時管理這兩個服務。這通常涉及設定像 PM2 這樣的進程管理器來運行後端伺服器,並使用 Nginx 作為反向代理來處理前端和後端的傳入請求。
使用下列指令安裝 PM2:
npx prisma init
對了,這就是我們的 Linux 環境設定。在下一節中,我們將從 GitHub 將程式碼庫下載到遠端伺服器上,並配置 Nginx 伺服器以使我們的應用程式在線上運行。
我假設您已經知道如何導航命令列。如果沒有你可以穀歌一下。我們將更改目錄並管理文件。首先克隆 GitHub 儲存庫和您的專案並將其複製到遠端伺服器上。使用以下指令作為參考:
datasource db { provider = "sqlite" url = "file:./dev.db" } generator client { provider = "prisma-client-js" } model WatchlistItem { id Int @id @default(autoincrement()) name String image String rating Float description String releaseDate DateTime genre String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
我們的 Web 應用程式檔案將儲存在 /var/www 內。您可以使用命令 ls(用於列出檔案)和 pwd(用於列印工作目錄)來查看 Linux 作業系統上的所有檔案。要了解有關 Linux 命令行的更多信息,請查看適用於初學者的 Linux 命令行教程。
現在我們的應用程式已經位於遠端伺服器上,我們可以建立後端和前端的生產版本。為此,我們只需 cd 進入 watchlist-tracker-app 專案內資料夾後端和客戶端的根目錄,然後執行如下所示的命令:
npx prisma migrate dev --name init
我們使用 Bun 作為運行時,因此我們將使用 Bun 命令進行安裝和建置步驟。
好吧,現在讓我們來設定我們的 Nginx 伺服器。我們將使用 nano 終端編輯器在文件內編寫程式碼。在終端機中執行此命令,為我們的監視清單追蹤器應用程式開啟 Nginx 檔案:
import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { PrismaClient } from '@prisma/client'; const app = new Hono(); app.use(cors()); const prisma = new PrismaClient(); app.get('/watchlist', async (c) => { const items = await prisma.watchlistItem.findMany(); return c.json(items); }); app.get('/watchlist/:id', async (c) => { const id = c.req.param('id'); const item = await prisma.watchlistItem.findUnique({ where: { id: Number(id) }, }); return item ? c.json(item) : c.json({ error: 'Item not found' }, 404); }); app.post('/watchlist', async (c) => { const data = await c.req.json(); const newItem = await prisma.watchlistItem.create({ data }); return c.json(newItem); }); app.put('/watchlist/:id', async (c) => { const id = c.req.param('id'); const data = await c.req.json(); const updatedItem = await prisma.watchlistItem.update({ where: { id: Number(id) }, data, }); return c.json(updatedItem); }); app.delete('/watchlist/:id', async (c) => { const id = c.req.param('id'); await prisma.watchlistItem.delete({ where: { id: Number(id) } }); return c.json({ success: true }); }); Bun.serve({ fetch: app.fetch, port: 8000, }); console.log('Server is running on http://localhost:8000'); export default app;
如果您不熟悉 Nano 程式碼編輯器,請查看此備忘單。
您只需將此配置複製並貼上到文件中並儲存即可。請務必將 server_name IP 位址替換為您自己的 Hetzner IP 位址:
"scripts": { "start": "bun run src/server.ts", "build": "bun build src/server.ts --outdir ./dist --target node" },
Nginx 常用來扮演反向代理伺服器的角色。反向代理位於客戶端(使用者的瀏覽器)和後端伺服器之間。它接收來自客戶端的請求並將其轉發到一台或多台後端伺服器。後端處理完請求後,反向代理會將回應轉回給客戶端。在這樣的設定中,Nginx 將成為傳入流量的入口點,並將請求路由到特定服務,例如 Vite 前端或 API 後端。
Vite 生產預覽版本在連接埠 4173 上運行,我們的後端伺服器在連接埠 8000 上運行。如果您變更這些值,請確保也在這個 Nginx 設定檔中更新它們;否則伺服器將無法運作。
如果尚未使用此命令啟用 Nginx 站點,請啟用它:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
現在測試 Nginx 配置以確保沒有語法錯誤,並重新啟動 Nginx 以使用以下命令應用更改:
bun add hono prisma @prisma/client
我們快完成了。只剩下一步了。如果您現在在瀏覽器中存取您的 Hetzner IP 位址,您應該會看到類似 502 Bad Gateway 的錯誤。那是因為我們還沒有運行伺服器;首先,我們需要使用 PM2 同時運行兩台伺服器。因此,我們必須將PM2設定為在系統啟動時啟動,以便我們的應用程式始終在線。透過在終端機中執行以下命令來執行此操作:
npx prisma init
現在,我們必須讓後端和前端伺服器運作。從資料夾的根目錄中執行這些命令。
讓我們從後端伺服器開始,因此在終端機中執行以下命令:
datasource db { provider = "sqlite" url = "file:./dev.db" } generator client { provider = "prisma-client-js" } model WatchlistItem { id Int @id @default(autoincrement()) name String image String rating Float description String releaseDate DateTime genre String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
最後,讓我們運行客戶端前端伺服器,因此在終端機中執行以下命令:
npx prisma migrate dev --name init
您可以執行命令pm2 status來檢查兩台伺服器是否在線上並正在運行,如下所示。若要了解所有其他 PM2 指令,請閱讀 PM2 流程管理快速入門文件:
您可以透過在終端機中執行這些curl命令來測試伺服器是否可存取。如果它們正常工作,您應該取回 HTML 程式碼:
import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { PrismaClient } from '@prisma/client'; const app = new Hono(); app.use(cors()); const prisma = new PrismaClient(); app.get('/watchlist', async (c) => { const items = await prisma.watchlistItem.findMany(); return c.json(items); }); app.get('/watchlist/:id', async (c) => { const id = c.req.param('id'); const item = await prisma.watchlistItem.findUnique({ where: { id: Number(id) }, }); return item ? c.json(item) : c.json({ error: 'Item not found' }, 404); }); app.post('/watchlist', async (c) => { const data = await c.req.json(); const newItem = await prisma.watchlistItem.create({ data }); return c.json(newItem); }); app.put('/watchlist/:id', async (c) => { const id = c.req.param('id'); const data = await c.req.json(); const updatedItem = await prisma.watchlistItem.update({ where: { id: Number(id) }, data, }); return c.json(updatedItem); }); app.delete('/watchlist/:id', async (c) => { const id = c.req.param('id'); await prisma.watchlistItem.delete({ where: { id: Number(id) } }); return c.json({ success: true }); }); Bun.serve({ fetch: app.fetch, port: 8000, }); console.log('Server is running on http://localhost:8000'); export default app;
前往您的 Hetzner 伺服器的 IP 位址。如果您所做的一切正確,您應該會看到您的應用程式已部署並在線!網站通常有域名,而 IP 位址在搜尋欄中仍然隱藏。對於開發人員來說,購買網域然後更改名稱伺服器以將其連接到主機的伺服器是相當常見的。這超出了本教學的範圍,但您可以透過 Google 搜尋輕鬆學習如何操作。 Namecheap 是我首選的網域註冊商。
是的,我們已經非常接近完成了。現在,最後一步是使用 DeployHQ 來簡化部署流程。 DeployHQ 讓部署變得簡單,並且更適合安全目的。在線上伺服器上更新程式碼庫的傳統方法是使用 git pull 從 GitHub 儲存庫取得最新變更。然而,執行 git pull 並不是一個好的做法,因為它可能會暴露 git 資料夾,網站很可能不會被縮小、醜化等。
DeployHQ 在這裡發揮著至關重要的作用。它將修改後的檔案安全地複製到配置的資料夾中,確保伺服器上的 git 日誌中看不到任何變更。這看起來像是一種權衡,但它是一項安全功能,可以讓您放心部署的安全性。如果您熟悉 Vercel 或 Netlify 等平台,您會發現這些自動部署非常相似。在這種情況下,您的設定可以與 VPS 上的任何線上伺服器一起使用。
值得一提的是,我們為線上 Linux 遠端伺服器建立一個非 root 使用者非常重要。以 root 使用者身分登入並不總是最佳實踐;最好為另一個使用者設定類似的權限。 DeployHQ 也不鼓勵使用 root 使用者登入。為了讓 DeployHQ 正常運作,我們需要使用 SSH 登入我們的帳戶。我們將在擁有 DeployHQ 帳戶後執行此操作,因為我們必須將 DeployHQ SSH 公鑰放在我們的線上 Ubuntu 伺服器上。
首次註冊時,DeployHQ 讓您在 10 天內免費使用其所有功能,而無需承擔任何義務。之後,您的帳戶將恢復為免費計劃,允許您每天最多部署單一項目 5 次。
首先造訪 DeployHQ 網站並透過點擊下面看到的按鈕之一建立帳戶:
好的,您現在應該看到歡迎來到 DeployHQ 螢幕,並帶有「建立專案」按鈕。點選按鈕建立一個項目,如下所示:
在下一個畫面上,您需要建立一個新項目。因此,請為其命名,然後選擇您的 GitHub 儲存庫。另外,為您的專案選擇一個區域,然後建立一個專案:
您現在應該看到伺服器螢幕,如下所示。這意味著是時候為我們的遠端伺服器建立另一個 Linux 用戶了,這樣我們就不必依賴 root 用戶了。
先以 root 使用者身分登入伺服器,然後使用下面的指令建立新使用者。將 new_username 替換為您要用於新使用者的使用者名稱:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
系統會要求您設定密碼,並會提示輸入您的全名、房間號等詳細信息,您只需設定密碼即可。您可以跳過其他提示步驟,按 Enter 鍵將其留空,直到全部消失。
將新使用者新增至 sudo 群組也是一個好主意,這樣他們就可以擁有像 root 使用者一樣的管理權限。使用此處顯示的命令執行此操作:
mkdir backend cd backend bun init -y mkdir src touch src/server.ts
新用戶現在需要伺服器的 SSH 存取權限。首先切換到新用戶,然後使用以下命令為其建立 .ssh 目錄:
bun add hono prisma @prisma/client
現在我們必須將本機公鑰新增至伺服器上的authorized_keys中,以便在本機上使用以下指令複製您的公鑰:
npx prisma init
現在在伺服器上開啟authorized_keys文件,以便可以使用nano編輯器進行編輯:
datasource db { provider = "sqlite" url = "file:./dev.db" } generator client { provider = "prisma-client-js" } model WatchlistItem { id Int @id @default(autoincrement()) name String image String rating Float description String releaseDate DateTime genre String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
將複製的公鑰貼到此文件中。在儲存檔案之前,將 DeployHQ SSH 金鑰從伺服器頁面複製並貼上到相同檔案中。當您選取使用 SSH 金鑰而不是密碼進行身份驗證? 複選框時,您可以看到 SSH 金鑰。現在儲存檔案並使用以下命令為檔案設定正確的權限:
npx prisma migrate dev --name init
您現在可以測試新使用者的 SSH 登入。首先,從遠端伺服器登出,然後嘗試再次登錄,但這次使用您創建的新用戶而不是 root。請參閱下面的範例:
import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { PrismaClient } from '@prisma/client'; const app = new Hono(); app.use(cors()); const prisma = new PrismaClient(); app.get('/watchlist', async (c) => { const items = await prisma.watchlistItem.findMany(); return c.json(items); }); app.get('/watchlist/:id', async (c) => { const id = c.req.param('id'); const item = await prisma.watchlistItem.findUnique({ where: { id: Number(id) }, }); return item ? c.json(item) : c.json({ error: 'Item not found' }, 404); }); app.post('/watchlist', async (c) => { const data = await c.req.json(); const newItem = await prisma.watchlistItem.create({ data }); return c.json(newItem); }); app.put('/watchlist/:id', async (c) => { const id = c.req.param('id'); const data = await c.req.json(); const updatedItem = await prisma.watchlistItem.update({ where: { id: Number(id) }, data, }); return c.json(updatedItem); }); app.delete('/watchlist/:id', async (c) => { const id = c.req.param('id'); await prisma.watchlistItem.delete({ where: { id: Number(id) } }); return c.json({ success: true }); }); Bun.serve({ fetch: app.fetch, port: 8000, }); console.log('Server is running on http://localhost:8000'); export default app;
假設您所做的一切正確,您現在應該能夠使用新使用者登入。
現在我們終於可以完成伺服器表單了。使用您的資訊填寫表格,請參閱下面的範例:
名稱:watchlist-tracker-app
協定:SSH/SFTP
主機名稱:11.11.111.111
港口:22
使用者名稱:新使用者名稱
使用 SSH 金鑰而不是密碼進行身份驗證? : 已選取
部署路徑:/var/www/watchlist-tracker-app
部署路徑應該是伺服器上 GitHub 儲存庫所在的位置。現在您可以繼續建立伺服器。如果您遇到任何問題,則可能是由於您的防火牆設定造成的,因此請閱讀有關我應該允許哪些 IP 位址通過防火牆?的文檔。
您現在應該看到如下所示的「新部署」畫面:
成功部署後,您應該會看到以下畫面:
最後一步是設定自動部署,以便當您將本機儲存庫中的變更推送到 GitHub 時,它們會自動部署到遠端伺服器。您可以從「自動部署」頁面執行此操作,該頁面位於 DeployHQ 帳戶的左側側邊欄。請參閱此處的範例:
我們都完成了;恭喜,您已經學會瞭如何構建全棧 React 應用程式、將代碼庫部署到 GitHub、在運行 Linux Ubuntu 的 VPS 上託管應用程序,以及使用 DeployHQ 設定自動部署。您的開發者遊戲已升級!
使用現代工具和技術建立像 Watchlist Tracker 這樣的全端 CRUD 應用程式是高效且令人愉快的。我們使用了相當強大的技術堆疊:後端的 Bun、Hono、Prisma ORM 和 SQLite,而前端的 Vite、Tailwind CSS 和 TanStack Router 有助於使其響應迅速且功能齊全。無論用戶身在何處,Hetzner 將確保應用程式可靠且效能良好。
透過 DeployHQ 部署,讓部署變得非常簡單。您只需將更新直接從 Git 儲存庫推送到雲端伺服器即可。您在儲存庫中所做的任何變更都將自動部署到您的生產伺服器,以便最新版本的應用程式生效。這可以節省時間,因為自動化部署減少了與部署相關的錯誤數量,因此值得將其添加到任何形式的開發工作流程中。
本教程應該幫助您使用像 Hetzner 這樣的 VPS 以及 DeployHQ 的自動 git 部署將各種應用程式部署到生產環境。
以上是使用 DeployHQ 將 React Watchlist Tracker 應用程式部署到生產環境的詳細內容。更多資訊請關注PHP中文網其他相關文章!