mioku-service-webui 4.0.6 → 4.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/system.ts +139 -142
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-service-webui",
3
- "version": "4.0.6",
3
+ "version": "4.0.8",
4
4
  "description": "Mioku WebUI 服务",
5
5
  "main": "index.ts",
6
6
  "type": "module",
package/system.ts CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  WEBUI_DIST,
33
33
  writeJsonFile,
34
34
  } from "./utils";
35
+ import { spawn } from "node:child_process";
35
36
 
36
37
  async function fetchJson(url: string): Promise<any> {
37
38
  const res = await fetch(url, {
@@ -516,17 +517,7 @@ async function getGitOriginUrl(dir: string): Promise<string> {
516
517
  }
517
518
 
518
519
  function resolveWebUIProjectDir(): string {
519
- const candidates = [
520
- path.join(process.cwd(), "mioku-webui"),
521
- path.join(process.cwd(), "webui"),
522
- ];
523
-
524
- for (const dir of candidates) {
525
- if (fs.existsSync(path.join(dir, "package.json"))) {
526
- return dir;
527
- }
528
- }
529
- return candidates[0];
520
+ return path.join(process.cwd(), "..", "mioku-webui");
530
521
  }
531
522
 
532
523
  function readInstalledWebUIVersion(): string {
@@ -1010,11 +1001,17 @@ export async function installManagedPackage(
1010
1001
  ...Object.keys(afterPkg?.devDependencies || {}),
1011
1002
  ];
1012
1003
  const installedName =
1013
- afterDeps.find((name) => !beforeDeps.has(name) &&
1014
- (name.startsWith("mioku-plugin-") || name.startsWith("mioku-service-"))) ||
1015
- pkgName;
1016
-
1017
- const packageJsonPath = path.join(NODE_MODULES_DIR, installedName, "package.json");
1004
+ afterDeps.find(
1005
+ (name) =>
1006
+ !beforeDeps.has(name) &&
1007
+ (name.startsWith("mioku-plugin-") || name.startsWith("mioku-service-")),
1008
+ ) || pkgName;
1009
+
1010
+ const packageJsonPath = path.join(
1011
+ NODE_MODULES_DIR,
1012
+ installedName,
1013
+ "package.json",
1014
+ );
1018
1015
  const packageJson = readPackageJson(packageJsonPath);
1019
1016
  const missingServices = checkDependentServices(packageJson);
1020
1017
 
@@ -1500,19 +1497,20 @@ async function fetchLatestWebUIUpdate(
1500
1497
  }
1501
1498
 
1502
1499
  const job = (async () => {
1503
- const projectDir = resolveWebUIProjectDir();
1504
1500
  const currentVersion = readInstalledWebUIVersion();
1505
- const pkg = readPackageJson(projectDir) || {};
1506
- const originUrl = (await getGitOriginUrl(projectDir)) || "";
1507
- const repoUrl = originUrl || getRepositoryFromPackage(pkg);
1508
- const repo = parseGitHubRepo(repoUrl);
1501
+ // mioku-webui repo is at mioku-lab/mioku-webui, used for GitHub API and dist download
1502
+ const repo = {
1503
+ owner: "mioku-lab",
1504
+ repo: "mioku-webui",
1505
+ fullName: "mioku-lab/mioku-webui",
1506
+ };
1509
1507
 
1510
1508
  const fallback: WebUIUpdateCheckResult = {
1511
1509
  currentVersion,
1512
1510
  latestVersion: currentVersion,
1513
1511
  releaseTag: "",
1514
1512
  releaseUrl: "",
1515
- sourceRepo: repo?.fullName || "",
1513
+ sourceRepo: repo.fullName,
1516
1514
  hasUpdates: false,
1517
1515
  canUpdate: false,
1518
1516
  assetName: "",
@@ -1520,13 +1518,6 @@ async function fetchLatestWebUIUpdate(
1520
1518
  checkedAt: Date.now(),
1521
1519
  };
1522
1520
 
1523
- if (!repo) {
1524
- return {
1525
- ...fallback,
1526
- error: "WebUI 仓库不是 GitHub,暂不支持自动检查更新",
1527
- };
1528
- }
1529
-
1530
1521
  const res = await fetch(
1531
1522
  `https://api.github.com/repos/${repo.owner}/${repo.repo}/releases/latest`,
1532
1523
  {
@@ -1612,101 +1603,36 @@ async function fetchLatestMiokuUpdate(
1612
1603
  const cwd = process.cwd();
1613
1604
  const rootPkg = readRootPackageJson() || {};
1614
1605
  const currentVersion = normalizeVersionSpec(rootPkg?.version || "unknown");
1615
- const originUrl = (await getGitOriginUrl(cwd)) || "";
1616
- const repoUrl = originUrl || getRepositoryFromPackage(rootPkg);
1617
- const repo = parseGitHubRepo(repoUrl);
1618
- const { currentBranch, targetRef, targetBranch } =
1619
- await resolveMiokuTargetRef(cwd);
1620
-
1621
- const fallback: MiokuUpdateCheckResult = {
1622
- currentVersion,
1623
- latestVersion: currentVersion,
1624
- sourceRepo: repo?.fullName || "",
1625
- currentBranch,
1626
- targetRef,
1627
- hasUpdates: false,
1628
- behind: 0,
1629
- changelog: [],
1630
- checkedAt: Date.now(),
1631
- };
1632
-
1633
- if (!fs.existsSync(path.join(cwd, ".git"))) {
1634
- return {
1635
- ...fallback,
1636
- error: isContainerRuntime()
1637
- ? "当前 Docker 容器未挂载 .git 到 /app/.git,无法检查 Mioku 更新"
1638
- : "当前目录不是 Git 仓库,无法检查 Mioku 更新",
1639
- };
1640
- }
1641
-
1642
- if (!repo) {
1643
- return {
1644
- ...fallback,
1645
- error: "Mioku 仓库不是 GitHub,暂不支持自动检查更新",
1646
- };
1647
- }
1648
-
1649
- const fetchRes = await runCommand(
1650
- "git",
1651
- ["fetch", "origin", targetBranch],
1652
- cwd,
1653
- );
1654
-
1655
- if (fetchRes.code !== 0) {
1656
- return {
1657
- ...fallback,
1658
- error: `git fetch 失败: ${fetchRes.stderr || fetchRes.stdout}`.trim(),
1659
- };
1660
- }
1661
-
1662
- const compare = await runCommand(
1663
- "git",
1664
- ["rev-list", "--left-right", "--count", `HEAD...${targetRef}`],
1665
- cwd,
1666
- );
1667
-
1668
- if (compare.code !== 0) {
1669
- return {
1670
- ...fallback,
1671
- error: `无法比较更新: ${compare.stderr || compare.stdout}`.trim(),
1672
- };
1673
- }
1674
-
1675
- const parts = compare.stdout
1676
- .trim()
1677
- .split(/\s+/)
1678
- .map((item) => Number(item));
1679
- const behind = Number.isFinite(parts[1]) ? parts[1] : 0;
1680
- const changelogRes = await runCommand(
1681
- "git",
1682
- ["log", "--oneline", `HEAD..${targetRef}`, "-n", "30"],
1683
- cwd,
1684
- );
1685
1606
 
1607
+ // Get latest version from npm registry
1686
1608
  let latestVersion = currentVersion;
1687
- const remotePkg = await runCommand(
1688
- "git",
1689
- ["show", `${targetRef}:package.json`],
1690
- cwd,
1691
- );
1692
- if (remotePkg.code === 0) {
1693
- try {
1694
- const parsed = JSON.parse(remotePkg.stdout);
1695
- latestVersion = normalizeVersionSpec(parsed?.version || currentVersion);
1696
- } catch {
1697
- latestVersion = currentVersion;
1609
+ try {
1610
+ const latestOutput = await runCommand(
1611
+ "bun",
1612
+ ["info", "mioku", "version"],
1613
+ cwd,
1614
+ );
1615
+ if (latestOutput.code === 0 && latestOutput.stdout.trim()) {
1616
+ latestVersion = normalizeVersionSpec(latestOutput.stdout.trim());
1698
1617
  }
1618
+ } catch {
1619
+ // ignore - keep currentVersion as fallback
1699
1620
  }
1700
1621
 
1622
+ const hasUpdates =
1623
+ latestVersion !== "unknown" &&
1624
+ currentVersion !== "unknown" &&
1625
+ isVersionNewer(latestVersion, currentVersion);
1626
+
1701
1627
  return {
1702
1628
  currentVersion,
1703
1629
  latestVersion,
1704
- sourceRepo: repo.fullName,
1705
- currentBranch,
1706
- targetRef,
1707
- hasUpdates: behind > 0,
1708
- behind,
1709
- changelog: changelogRes.stdout.trim().split("\n").filter(Boolean),
1630
+ sourceRepo: "mioku",
1631
+ currentBranch: "",
1632
+ targetRef: "",
1633
+ hasUpdates,
1634
+ behind: hasUpdates ? 1 : 0,
1635
+ changelog: [],
1710
1636
  checkedAt: Date.now(),
1711
1637
  error: "",
1712
1638
  };
@@ -1730,24 +1656,18 @@ export async function checkMiokuReleaseUpdate(
1730
1656
 
1731
1657
  export async function updateMiokuFromMain(): Promise<Record<string, any>> {
1732
1658
  const cwd = process.cwd();
1733
- if (!fs.existsSync(path.join(cwd, ".git"))) {
1734
- throw new Error(
1735
- isContainerRuntime()
1736
- ? "当前 Docker 容器未挂载 .git 到 /app/.git,无法更新 Mioku"
1737
- : "当前目录不是 Git 仓库,无法更新 Mioku",
1738
- );
1739
- }
1740
1659
 
1741
- const { targetBranch } = await resolveMiokuTargetRef(cwd);
1742
- const before = await runCommand("git", ["show", "HEAD:package.json"], cwd);
1743
- const pull = await runCommand("git", ["pull", "origin", targetBranch], cwd);
1660
+ const beforePkg = readRootPackageJson();
1661
+ const beforeVersion = beforePkg?.version || "unknown";
1744
1662
 
1745
- if (pull.code !== 0) {
1746
- throw new Error(`git pull 失败: ${pull.stderr || pull.stdout}`);
1663
+ const update = await runCommand("bun", ["update", "mioku"], cwd);
1664
+ if (update.code !== 0) {
1665
+ throw new Error(`bun update mioku 失败: ${update.stderr || update.stdout}`);
1747
1666
  }
1748
1667
 
1749
- const after = await runCommand("git", ["show", "HEAD:package.json"], cwd);
1750
- const changed = packageJsonChanged(before.stdout, after.stdout);
1668
+ const afterPkg = readRootPackageJson();
1669
+ const afterVersion = afterPkg?.version || beforeVersion;
1670
+ const changed = beforeVersion !== afterVersion;
1751
1671
 
1752
1672
  let reinstallOutput = "";
1753
1673
  if (changed) {
@@ -1773,6 +1693,33 @@ export async function updateMiokuFromMain(): Promise<Record<string, any>> {
1773
1693
  };
1774
1694
  }
1775
1695
 
1696
+ async function notifyOwnerWebUIDownloaded(version: string): Promise<void> {
1697
+ const miokuConfig = getMiokuConfig();
1698
+ const owners = miokuConfig?.owners || [];
1699
+ if (owners.length === 0) {
1700
+ return;
1701
+ }
1702
+
1703
+ const bots = Array.from(connectedBots.values());
1704
+ if (bots.length === 0) {
1705
+ return;
1706
+ }
1707
+
1708
+ const bot = bots[0];
1709
+ const message = `🎉 WebUI 已自动更新到 v${version},记得重启哦~`;
1710
+
1711
+ for (const ownerId of owners) {
1712
+ try {
1713
+ await bot.api("send_private_msg", {
1714
+ user_id: Number(ownerId),
1715
+ message,
1716
+ });
1717
+ } catch (err: any) {
1718
+ logger.warn(`[webui] 通知主人 ${ownerId} 失败: ${err?.message || err}`);
1719
+ }
1720
+ }
1721
+ }
1722
+
1776
1723
  export async function updateWebUIDistFromRelease(): Promise<
1777
1724
  Record<string, any>
1778
1725
  > {
@@ -1798,6 +1745,9 @@ export async function updateWebUIDistFromRelease(): Promise<
1798
1745
  );
1799
1746
  }
1800
1747
 
1748
+ let lastLogTime = 0;
1749
+ const LOG_INTERVAL_MS = 2000;
1750
+ const downloadUrl = check.assetUrl;
1801
1751
  const tempDir = path.join(
1802
1752
  os.tmpdir(),
1803
1753
  `mioku-webui-update-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
@@ -1810,20 +1760,65 @@ export async function updateWebUIDistFromRelease(): Promise<
1810
1760
 
1811
1761
  let backupPath = "";
1812
1762
  try {
1813
- const downloadUrl = check.proxiedAssetUrl || check.assetUrl;
1814
- const downloadRes = await fetch(downloadUrl, {
1815
- headers: {
1816
- Accept: "application/octet-stream",
1817
- "User-Agent": "mioku-webui-updater",
1818
- },
1819
- });
1763
+ logger.info(`[webui] 正在下载 WebUI dist...`);
1764
+
1765
+ await new Promise<void>((resolve, reject) => {
1766
+ const curl = spawn(
1767
+ "curl",
1768
+ [
1769
+ "-L",
1770
+ "-o",
1771
+ zipPath,
1772
+ "-A",
1773
+ "mioku-webui-updater/1.0",
1774
+ "--silent",
1775
+ "--show-error",
1776
+ downloadUrl,
1777
+ ],
1778
+ {
1779
+ stdio: ["ignore", "pipe", "pipe"],
1780
+ },
1781
+ );
1820
1782
 
1821
- if (!downloadRes.ok) {
1822
- throw new Error(`下载 dist 压缩包失败: HTTP_${downloadRes.status}`);
1823
- }
1783
+ let stderr = "";
1784
+ curl.stderr.on("data", (chunk) => {
1785
+ stderr += String(chunk);
1786
+ });
1787
+
1788
+ curl.on("close", (code) => {
1789
+ if (code === 0) {
1790
+ resolve();
1791
+ } else {
1792
+ reject(new Error(`curl 下载失败 (code ${code}): ${stderr}`));
1793
+ }
1794
+ });
1795
+
1796
+ curl.on("error", (err) => {
1797
+ reject(new Error(`curl 进程异常: ${err.message}`));
1798
+ });
1799
+
1800
+ // Periodically log progress by checking file size
1801
+ const progressTimer = setInterval(() => {
1802
+ if (fs.existsSync(zipPath)) {
1803
+ const stat = fs.statSync(zipPath);
1804
+ const now = Date.now();
1805
+ if (now - lastLogTime >= LOG_INTERVAL_MS) {
1806
+ logger.info(
1807
+ `[webui] 正在下载 WebUI dist... ${(stat.size / 1024 / 1024).toFixed(1)} MB 已下载`,
1808
+ );
1809
+ lastLogTime = now;
1810
+ }
1811
+ }
1812
+ }, 1000);
1813
+
1814
+ // Clear timer when done
1815
+ curl.on("close", () => clearInterval(progressTimer));
1816
+ });
1824
1817
 
1825
- const buffer = Buffer.from(await downloadRes.arrayBuffer());
1826
- fs.writeFileSync(zipPath, buffer);
1818
+ const zipStat = fs.statSync(zipPath);
1819
+ logger.info(
1820
+ `[webui] 下载完成 (${(zipStat.size / 1024 / 1024).toFixed(1)} MB),正在解压...`,
1821
+ );
1827
1822
 
1828
1823
  const zip = new AdmZip(zipPath);
1829
1824
  zip.extractAllTo(unpackDir, true);
@@ -1875,6 +1870,8 @@ export async function updateWebUIDistFromRelease(): Promise<
1875
1870
  error: "",
1876
1871
  };
1877
1872
 
1873
+ await notifyOwnerWebUIDownloaded(check.latestVersion);
1874
+
1878
1875
  return {
1879
1876
  ok: true,
1880
1877
  updated: true,