> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/zh-tw/development/dev-environment.md).

# 開發環境

設定本機開發環境並執行提交前自我檢查。

MyIP 是一個包含兩個部分的單一儲存庫：一個 **Vue 3** 位於下列路徑的單頁應用程式 `frontend/` 以及一個 **Express 5** 位於下列路徑的 API `api/` + `backend-server.js`。一個指令即可同時執行兩者。

## 先決條件

<table><thead><tr><th width="180">工具</th><th width="220">版本</th><th>備註</th></tr></thead><tbody><tr><td><strong>Node.js</strong></td><td>24</td><td>Docker 映像檔（<code>node:24-alpine</code>）與 CI 都使用的版本。</td></tr><tr><td><strong>pnpm</strong></td><td>固定在 <code>package.json</code></td><td>請不要手動安裝其他版本——請參見下方。</td></tr><tr><td><strong>Git</strong></td><td>任何較新的版本</td><td>貢獻分支從此處分出 <code>dev</code>.</td></tr></tbody></table>

取得正確 pnpm 的最簡單方式是使用 Corepack，它隨 Node 一同提供：

```bash
corepack enable
```

Corepack 會讀取 `packageManager` 中的欄位 `package.json` 並精確佈建該 pnpm 版本。Dockerfile 與 CI 工作流程也這麼做，因此你的本機工具鏈會與它們一致。

## 僅限 pnpm

{% hint style="danger" %}
**絕對不要執行 `npm install` 或 `yarn` 在這個儲存庫中。**
{% endhint %}

有三件事特別依賴 pnpm：

* **`packageManager` 位於 `package.json` 它鎖定了確切的 pnpm 版本。** Corepack、Dockerfile 與 GitHub Actions 工作流程都會讀取它，因此每個人解析出的依賴樹都相同。
* **`pnpm-lock.yaml` 會被提交。** npm 會寫入 `package-lock.json` 而 yarn 會寫入 `yarn.lock` —— 這會形成第二個相互競爭的 lockfile，而專案中沒有任何東西會讀取它。CI 會使用 `--frozen-lockfile`，因此一旦 pnpm lockfile 偏離，就會直接導致建置失敗。
* **`pnpm-workspace.yaml` 會保存安裝腳本核准** (`allowBuilds`）供少數允許執行 postinstall 腳本的套件使用。npm 與 yarn 完全忽略該檔案。

## 安裝並執行

{% stepper %}
{% step %}

#### 複製並安裝

```bash
git clone https://github.com/jason5ng32/MyIP.git
cd MyIP
pnpm install
```

{% endstep %}

{% step %}

#### 建立你的 `.env`

```bash
cp .env.example .env
```

其中的一切 `.env.example` 都可選，用於 *啟動* 應用程式，但若沒有 MaxMind 憑證，IP 地理定位仍然會失效。參見 [MaxMind 設定](/developer/zh-tw/getting-started/maxmind-setup.md) 以及 [環境變數](/developer/zh-tw/reference/environment-variables.md).
{% endstep %}

{% step %}

#### 啟動兩個部分

```bash
pnpm dev
```

這會並排執行 Vite 開發伺服器與後端（位於 `nodemon`）並透過 `concurrently`。編輯 `.vue` 檔案，Vite 會熱重載；編輯後端匯入的任何內容，nodemon 就會重新啟動 API。
{% endstep %}

{% step %}

#### 開啟應用程式

前往 `http://localhost:18966`。Vite 開發伺服器會將 `/api` 轉送到後端。
{% endstep %}
{% endstepper %}

## 連接埠

兩個埠號都來自 `.env` ，預設如下：

| 變數              | 預設      | 被用於                                         |
| --------------- | ------- | ------------------------------------------- |
| `FRONTEND_PORT` | `18966` | Vite 開發伺服器（`pnpm dev`）以及靜態伺服器（`pnpm start`) |
| `BACKEND_PORT`  | `11966` | Express API（`backend-server.js`)            |

Vite 開發伺服器會繫結到 `0.0.0.0`，因此你可以從區域網路中的其他裝置存取它，並會代理 `/api` → `http://localhost:<BACKEND_PORT>`。你很少需要直接連到後端埠號。

{% hint style="warning" %}
**對 `/api/*` 的請求需要一個 `Referer`.** 全域中介軟體（`requireReferer` 位於 `common/guards.js`）會拒絕 referer 不在允許清單中的請求。 `localhost` 是允許的，所以瀏覽器沒問題——但單純的 `curl http://localhost:11966/api/...` 會得到 `403`。 `請加上` ，在手動測試時使用。
{% endhint %}

## 本機 `.env` 備註

* `.env` 由 **兩個** 兩個部分載入： `backend-server.js` 在執行時，以及 `vite.config.js` 在建置時。
* 以前綴 `VITE_` 是 **在建置時寫入前端 bundle 中**。變更其中一個需要重新 `pnpm dev` 啟動，而不只是重新整理頁面——而且絕不要把密鑰放在 `VITE_` 名稱之後。
* 當變數為空時，選用整合會完全停用。沒有 Sentry DSN 就完全不會載入 Sentry 程式碼；沒有 Firebase 設定就不會擷取 SDK。你可以在幾乎空白的 `.env`.
* 記錄相關開關是 `LOG_LEVEL`, `LOG_FORMAT`，以及 `LOG_HTTP` ——請參見 [記錄](/developer/zh-tw/configuration/logging.md)。沒有 `NODE_ENV` 可在本專案中的任何位置切換。

## 每個指令碼

| 指令                    | 它的功能                           |
| --------------------- | ------------------------------ |
| `pnpm dev`            | Vite 開發伺服器 + 在 nodemon 下的後端，一起 |
| `pnpm build`          | 將正式版前端建置到 `dist/`              |
| `pnpm preview`        | Vite 的預覽伺服器，用於已建置的輸出           |
| `pnpm test`           | `node --test tests/*.test.js`  |
| `pnpm check`          | `測試` + `建置` —— 提交前的自我檢查        |
| `pnpm start`          | 靜態前端伺服器 + 後端（正式主機執行的內容）        |
| `pnpm start-backend`  | 僅後端                            |
| `pnpm start-frontend` | 僅靜態前端伺服器                       |

## 自我檢查

在你交付變更——PR、commit、或審查請求——之前，請執行：

```bash
pnpm check
```

也就是 `pnpm test` 接著執行 `pnpm build`。這與 CI 在每次 push 與針對 `main` 以及 `dev`的 pull request 上執行的兩個步驟相同，因此本機通過的 `檢查` 通常代表 CI 會綠燈。

{% hint style="info" %}
**視覺變更無法自我測試。** Node 測試執行器不會渲染 Vue 元件，也不會驅動瀏覽器。如果你的變更屬於視覺層面，請在 PR 中明確說明，並讓人手動在 `pnpm dev`。請參閱 [測試](/developer/zh-tw/development/testing.md).
{% endhint %}

## 開發時的實用加值功能

* **行動版主控台。** 在手機或平板上， `pnpm dev` 會自動載入 vConsole——一個螢幕上的開發工具面板。它僅供開發且僅限行動裝置；絕不會隨建置發佈。
* **點擊看來源。** 這個 `code-inspector-plugin` 已接入開發伺服器，因此你可以從瀏覽器中的元素跳到編輯器裡對應的原始碼行。
* **額外的開發主機。** `vite.config.js` 允許 `dev.ipcheck.ing` 以及 `test.ipcheck.ing` 除了 localhost 之外，用來針對真實主機名稱進行測試。

## 下一節

* [程式設計慣例](/developer/zh-tw/development/coding-conventions.md) —— 你的變更預期要遵守的規則。
* [新增工具](/developer/zh-tw/development/adding-a-new-tool.md) —— 端到端操作導覽。
* [專案結構](/developer/zh-tw/architecture/project-structure.md) —— 哪些內容放在哪裡。
* [如何貢獻](/developer/zh-tw/contributing/how-to-contribute.md) —— 分支紀律與 PR 指引。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/zh-tw/development/dev-environment.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
