From c6a206ae06366d617ad671d348cb9458b836503b Mon Sep 17 00:00:00 2001 From: steponeerror Date: Sun, 18 Oct 2026 11:19:09 +0800 Subject: [PATCH 1/4] extension: switch icons to repo avatar --- extension/public/icons/icon-128.png | Bin 24956 -> 8782 bytes extension/public/icons/icon-32.png | Bin 3045 -> 1305 bytes extension/public/icons/icon-64.png | Bin 9200 -> 3522 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/extension/public/icons/icon-128.png b/extension/public/icons/icon-128.png index b20935c4f25388e59312d8f417f93857d48ccd99..7e7e3eeb8a6fd0bcde616c580b51b3454a719339 100644 GIT binary patch literal 8782 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 wa6)Rp2@p!@a0u-gC_OW0&cJk9+bJ!FGSh)c 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 Date: Sun, 18 Oct 2026 11:19:09 +0800 Subject: [PATCH 2/4] extension: Firefox channel build (derived manifest, AMO unlisted) --- extension/.gitignore | 2 + extension/README.md | 10 +++++ extension/package.json | 1 + extension/scripts/make-firefox-manifest.mjs | 26 +++++++++++++ .../scripts/make-firefox-manifest.test.mjs | 19 ++++++++++ extension/src/UpdateCheck.tsx | 7 +++- .../src/__tests__/background-firefox.test.ts | 38 +++++++++++++++++++ extension/src/background.ts | 8 ++-- 8 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 extension/scripts/make-firefox-manifest.mjs create mode 100644 extension/scripts/make-firefox-manifest.test.mjs create mode 100644 extension/src/__tests__/background-firefox.test.ts diff --git a/extension/.gitignore b/extension/.gitignore index 3a77e8c..d0d0f6c 100644 --- a/extension/.gitignore +++ b/extension/.gitignore @@ -1,3 +1,5 @@ node_modules/ dist/ *.zip +web-ext-artifacts/ +amo.env diff --git a/extension/README.md b/extension/README.md index 0143dd2..217ec4f 100644 --- a/extension/README.md +++ b/extension/README.md @@ -19,6 +19,16 @@ npm run build 也可解壓 `ip-radar-extension.zip`,載入解壓後包含 `manifest.json` 的資料夾。 +## Firefox + +在 `extension` 目錄執行 `npm run build:firefox`:產出 Firefox 版 `dist`(manifest 由 Chrome 版派生,`background` 改為事件頁並加入 `browser_specific_settings`),並打包 `IP.Radar.Firefox_v<版本>.zip`。 + +- 臨時安裝:Firefox 開啟 `about:debugging#/runtime/this-firefox`,按「Load Temporary Add-on」,選擇 `dist` 內的 `manifest.json`。臨時附加元件重啟 Firefox 後即消失。 +- 永久安裝需 AMO 簽名(免費,unlisted 不會公開上架):建立 `amo.env`(內含 `WEB_EXT_API_KEY`、`WEB_EXT_API_SECRET`),執行 `set -a; . amo.env; set +a` 後再 `npx web-ext sign --source-dir dist --channel unlisted`,簽好的 XPI 在 `web-ext-artifacts/`。 +- 新版本重複簽名上傳後,已安裝者由 AMO 自動更新。 +- gecko id 為 `ip-radar-extension@steponeerror.github.io`,一經簽名即永久綁定,不可更換。 +- Firefox 通道的「檢查更新」比對 Release 的 `IP.Radar.Firefox_*.zip` 資產。 + ## 功能 - 一批預設最多 100 個 IPv4 / IPv6,可在設定調整單次最大查詢數(1–1,000);換行、逗號、分號或空格分隔,去重並提示無效項目。 diff --git a/extension/package.json b/extension/package.json index 0bb8691..91516b6 100644 --- a/extension/package.json +++ b/extension/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite --host 127.0.0.1", "build": "tsc --noEmit && vite build", + "build:firefox": "VITE_CHANNEL=firefox npm run build && node scripts/make-firefox-manifest.mjs && cd dist && zip -qr ../IP.Radar.Firefox_v$npm_package_version.zip .", "test": "vitest run" }, "dependencies": { diff --git a/extension/scripts/make-firefox-manifest.mjs b/extension/scripts/make-firefox-manifest.mjs new file mode 100644 index 0000000..7d10308 --- /dev/null +++ b/extension/scripts/make-firefox-manifest.mjs @@ -0,0 +1,26 @@ +// Firefox manifest 从 Chrome 版派生:单一事实源 public/manifest.json,不双维护 +import { readFile, writeFile } from "node:fs/promises"; +import { pathToFileURL } from "node:url"; + +const GECKO_ID = "ip-radar-extension@steponeerror.github.io"; + +export function firefoxManifest(manifest) { + return { + ...manifest, + // Firefox MV3 用事件页(background.scripts),不支持 service_worker;type:module 必须 + // 保留:background.js 与 index 共享 rollup 代码分块,顶层有 import + background: { scripts: ["background.js"], type: "module" }, + browser_specific_settings: { + gecko: { id: GECKO_ID, strict_min_version: "127.0" }, // 127 = optional_host_permissions + }, + }; +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + const manifest = JSON.parse(await readFile("public/manifest.json", "utf8")); + await writeFile( + "dist/manifest.json", + JSON.stringify(firefoxManifest(manifest), null, 2) + "\n", + ); + console.log(`dist/manifest.json → Firefox (gecko id ${GECKO_ID})`); +} diff --git a/extension/scripts/make-firefox-manifest.test.mjs b/extension/scripts/make-firefox-manifest.test.mjs new file mode 100644 index 0000000..04e0ba1 --- /dev/null +++ b/extension/scripts/make-firefox-manifest.test.mjs @@ -0,0 +1,19 @@ +import { readFileSync } from "node:fs"; +import { test, expect } from "vitest"; +import { firefoxManifest } from "./make-firefox-manifest.mjs"; + +test("firefox manifest derives from the chrome manifest", () => { + const chrome = JSON.parse( + readFileSync(new URL("../public/manifest.json", import.meta.url), "utf8"), + ); + const fx = firefoxManifest(chrome); + expect(fx.background).toEqual({ scripts: ["background.js"], type: "module" }); + expect(fx.browser_specific_settings.gecko).toEqual({ + id: "ip-radar-extension@steponeerror.github.io", + strict_min_version: "127.0", + }); + // 除 background/gecko 外逐字段继承 Chrome manifest:权限/图标/popup 不双维护 + const { background, browser_specific_settings, ...rest } = fx; + const { background: chromeBackground, ...chromeRest } = chrome; + expect(rest).toEqual(chromeRest); +}); diff --git a/extension/src/UpdateCheck.tsx b/extension/src/UpdateCheck.tsx index 2d4d5e8..582b408 100644 --- a/extension/src/UpdateCheck.tsx +++ b/extension/src/UpdateCheck.tsx @@ -10,6 +10,11 @@ const RELEASES_API = "https://api.github.com/repos/steponeerror/ip-radar-desktop/releases/latest"; const RELEASES_URL = "https://github.com/steponeerror/ip-radar-desktop/releases/latest"; +// 通道资产前缀:Firefox 构建(VITE_CHANNEL=firefox)比对 Firefox zip,默认 Chrome +const ASSET_PREFIX = + import.meta.env.VITE_CHANNEL === "firefox" + ? "IP.Radar.Firefox_" + : "IP.Radar.Chrome_"; export function UpdateCheck() { const { t } = useI18n(); const [busy, setBusy] = useState(false); @@ -39,7 +44,7 @@ export function UpdateCheck() { !Array.isArray(release.assets) || !release.assets.some( (asset: { name?: string }) => - asset.name?.startsWith("IP.Radar.Chrome_") && asset.name.endsWith(".zip"), + asset.name?.startsWith(ASSET_PREFIX) && asset.name.endsWith(".zip"), ) ) { setMessage("新版本的擴充功能下載尚未就緒,請稍後再試。"); diff --git a/extension/src/__tests__/background-firefox.test.ts b/extension/src/__tests__/background-firefox.test.ts new file mode 100644 index 0000000..493478f --- /dev/null +++ b/extension/src/__tests__/background-firefox.test.ts @@ -0,0 +1,38 @@ +import { it, expect, vi, afterEach } from "vitest"; +afterEach(() => vi.unstubAllGlobals()); +// Firefox 没有 storage.local.setAccessLevel;initialize 必须跳过它并照常完成其余初始化。 +// 独立文件:background 模块的监听注册副作用需要全新的模块注册表。 +it("initialize survives without setAccessLevel (Firefox)", async () => { + let onInstalled: Function = () => {}; + const setPopup = vi.fn(); + let state: Record = {}; + vi.stubGlobal("chrome", { + runtime: { + id: "test", + getURL: (p: string) => `chrome-extension://test/${p}`, + onMessage: { addListener: vi.fn() }, + onInstalled: { addListener: (f: Function) => (onInstalled = f) }, + onStartup: { addListener: vi.fn() }, + }, + storage: { + local: { + get: async () => state, + set: async (v: Record) => { + state = v; + }, + // 故意不提供 setAccessLevel + }, + }, + alarms: { + create: vi.fn(), + clear: vi.fn(), + onAlarm: { addListener: vi.fn() }, + }, + action: { setPopup, onClicked: { addListener: vi.fn() } }, + tabs: { create: vi.fn() }, + }); + await import("../background"); + onInstalled(); + await vi.waitFor(() => expect(setPopup).toHaveBeenCalled()); + expect(setPopup).toHaveBeenCalledWith({ popup: "index.html?mode=popup" }); +}); diff --git a/extension/src/background.ts b/extension/src/background.ts index 94b1955..4cc52fa 100644 --- a/extension/src/background.ts +++ b/extension/src/background.ts @@ -147,9 +147,11 @@ chrome.action.onClicked.addListener(() => { }); }); async function initialize() { - await chrome.storage.local.setAccessLevel({ - accessLevel: "TRUSTED_CONTEXTS", - }); + // Firefox 没有 setAccessLevel(Chrome 专属);缺省即仅受信上下文可读,跳过不阻塞其余初始化 + if (chrome.storage.local.setAccessLevel) + await chrome.storage.local.setAccessLevel({ + accessLevel: "TRUSTED_CONTEXTS", + }); const state = await read(); await chrome.action.setPopup({ popup: state.settings.openInTab ? "" : "index.html?mode=popup", From dfa43daf9bd0d40f4d6af244c7e44fb8ebcb794f Mon Sep 17 00:00:00 2001 From: steponeerror Date: Sun, 18 Oct 2026 11:23:32 +0800 Subject: [PATCH 3/4] =?UTF-8?q?docs:=20ADR=200002=20Firefox=20=E9=80=9A?= =?UTF-8?q?=E9=81=93=20AMO=20unlisted=20=E5=88=86=E5=8F=91=20+=20CONTEXT?= =?UTF-8?q?=20=E9=80=9A=E9=81=93=E8=AF=8D=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTEXT.md | 4 ++++ .../0002-firefox-channel-amo-unlisted-derived-manifest.md | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 docs/adr/0002-firefox-channel-amo-unlisted-derived-manifest.md diff --git a/CONTEXT.md b/CONTEXT.md index 23c13f3..8c4a3e8 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -36,3 +36,7 @@ _Avoid_: 下载、匯出 **L1 零泄漏**: 左栏 IP 列表(扫描层)只显示共识徽章与多源一致国家;任何单源数据(运营商、分数、城市)不进 L1,留在详情层。 _Avoid_: 列表层泄漏 + +**通道**: +同一套插件代码面向特定浏览器(Chrome/Firefox)的发行变体;各有独立的 manifest 派生与「检查更新」比对的 Release 资产前缀。 +_Avoid_: 浏览器版、浏览器分支 diff --git a/docs/adr/0002-firefox-channel-amo-unlisted-derived-manifest.md b/docs/adr/0002-firefox-channel-amo-unlisted-derived-manifest.md new file mode 100644 index 0000000..1adf831 --- /dev/null +++ b/docs/adr/0002-firefox-channel-amo-unlisted-derived-manifest.md @@ -0,0 +1,8 @@ +# Firefox 通道经 AMO unlisted 签名分发,manifest 构建期派生 + +Chrome 与 Firefox 共用 `extension/` 同一套代码,`public/manifest.json` 是唯一 manifest 事实源(Chrome 版原样);Firefox 版由 `scripts/make-firefox-manifest.mjs` 在构建时派生 `dist/manifest.json`:background 以事件页 `scripts` 取代 `service_worker`(`type: module` 必须保留——background.js 与 index 共享 rollup 代码分块,顶层有 import),并加入 `browser_specific_settings.gecko`(id=`ip-radar-extension@steponeerror.github.io`,strict_min_version=127,即 optional_host_permissions 的支持门槛)。发行走 AMO unlisted 自签名(免费,不公开上架):`web-ext sign` 产物 xpi 直接分发,新版本重复上传签名后,已安装者由 AMO 更新服务自动更新。唯一不可逆点:gecko id 首次签名后即为 AMO 永久身份,换 id 等于发布一个新扩展。 + +## Considered Options + +- 未签名 zip + `about:debugging` 临时加载 — 否决:重启即失,日常自用不可接受。 +- 单 manifest 双写 background(`service_worker`+`scripts` 并存)— 否决:Chrome 侧产生未知键告警,公共 manifest 混入通道细节;派生保 Chrome 版零污染。 From b0915112ea5b9c8113d44ab0d54e4371c0c4e091 Mon Sep 17 00:00:00 2001 From: steponeerror Date: Sun, 18 Oct 2026 11:29:31 +0800 Subject: [PATCH 4/4] extension: fresh zip per build:firefox + release asset note --- extension/README.md | 1 + extension/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extension/README.md b/extension/README.md index 217ec4f..5a073dc 100644 --- a/extension/README.md +++ b/extension/README.md @@ -28,6 +28,7 @@ npm run build - 新版本重複簽名上傳後,已安裝者由 AMO 自動更新。 - gecko id 為 `ip-radar-extension@steponeerror.github.io`,一經簽名即永久綁定,不可更換。 - Firefox 通道的「檢查更新」比對 Release 的 `IP.Radar.Firefox_*.zip` 資產。 +- 發佈時需在 GitHub Release 上傳名為 `IP.Radar.Firefox_v<版本>.zip` 的資產,Firefox 端「檢查更新」才會提示就緒;已簽名安裝者由 AMO 自動更新。 ## 功能 diff --git a/extension/package.json b/extension/package.json index 91516b6..24c1641 100644 --- a/extension/package.json +++ b/extension/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "vite --host 127.0.0.1", "build": "tsc --noEmit && vite build", - "build:firefox": "VITE_CHANNEL=firefox npm run build && node scripts/make-firefox-manifest.mjs && cd dist && zip -qr ../IP.Radar.Firefox_v$npm_package_version.zip .", + "build:firefox": "VITE_CHANNEL=firefox npm run build && node scripts/make-firefox-manifest.mjs && rm -f IP.Radar.Firefox_v$npm_package_version.zip && cd dist && zip -qr ../IP.Radar.Firefox_v$npm_package_version.zip .", "test": "vitest run" }, "dependencies": {