diff --git a/.github/workflows/fnos-build-fpk.yml b/.github/workflows/fnos-build-fpk.yml index 25fec8dc..05c6a338 100644 --- a/.github/workflows/fnos-build-fpk.yml +++ b/.github/workflows/fnos-build-fpk.yml @@ -5,12 +5,12 @@ name: Build Octop FPK (template) # # then publishes GitHub releases (fixed version + rolling latest). # # # # To enable on your fork: -# # 1. Replace ghcr.io/TencentCloud/octop with your own image namespace (or fork a copy). +# # 1. Replace jubaoliang/octop with your own image namespace (or fork a copy). # # 2. Optionally add a push trigger below (e.g. branches: [main]) so a rebuild happens # # automatically on every upstream sync. It is intentionally workflow_dispatch-only # # here so it never runs on the upstream repository. # # -# image job:从仓库源码构建 Octop Docker 镜像,推送到 GHCR(ghcr.io/TencentCloud/octop:vanilla) +# image job:从仓库源码构建 Octop Docker 镜像,推送到 Docker Hub(jubaoliang/octop:vanilla) # fpk job:构建「Docker 版」飞牛安装包 Fnos-octop-vanilla--.fpk(依赖 image,确保镜像已就绪) # native job:构建「本地版(非 Docker)」飞牛安装包 Fnos-octop-vanilla-native--.fpk。 # 不内置 Python 运行时(依赖飞牛应用商店 Python 3.12), @@ -85,12 +85,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Login to GHCR + - name: Login to Docker Hub uses: docker/login-action@v3 with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + # Docker Hub(与 docker-publish.yml 同一凭据,DOCKERHUB_USERNAME 为 jubaoliang 时推送 jubaoliang/octop) + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push image uses: docker/build-push-action@v6 @@ -99,8 +99,9 @@ jobs: file: fnos/docker/Dockerfile push: true tags: | - ghcr.io/TencentCloud/octop:vanilla - ghcr.io/TencentCloud/octop:vanilla-v${{ needs.version.outputs.version }} + jubaoliang/octop:latest + jubaoliang/octop:vanilla + jubaoliang/octop:vanilla-v${{ needs.version.outputs.version }} cache-from: type=gha cache-to: type=gha,mode=max @@ -304,7 +305,7 @@ jobs: echo "## 安装说明" echo "" echo "1. 飞牛应用中心 → 设置 → 手动安装应用,选择对应 .fpk。" - echo "2. Docker 版 **Fnos-octop-vanilla-${VER}-${ITER}.fpk**(约 80KB):飞牛自动从 GHCR 拉取镜像运行,镜像为 \\"ghcr.io/TencentCloud/octop:vanilla\\"。" + echo "2. Docker 版 **Fnos-octop-vanilla-${VER}-${ITER}.fpk**(约 80KB):飞牛自动从 Docker Hub 拉取镜像运行,镜像为 \\"jubaoliang/octop:vanilla\\"。" echo "3. 本地版 **Fnos-octop-vanilla-native-${VER}-${ITER}.fpk**(约 200MB):非 Docker,原生运行在飞牛主机;安装时自动关联系统 Python 3.12 开发工具;浏览器、远程桌面等附加组件在 Octop 应用内按需安装。" echo "" echo "---" diff --git a/CHANGELOG.md b/CHANGELOG.md index 267c1cfc..f75bddd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ ## [Unreleased] +## [0.9.28] - 2026-08-26 + +### 修复 + +- 无更新权限时隐藏检查更新入口 +- `/compact` 兼容 `.octop/conversation_history/` 卸载路径 +- FnOS 镜像改为 Docker Hub `jubaoliang/octop` + ### 新增 - 基层医生学习助手增加普通医学问答快路径、国内专业学会/专科分会与国际指南精确路由,并完善受控信源降级和检索预算。 diff --git a/Makefile b/Makefile index 37ac43ee..cdf862e8 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,8 @@ UV := $(shell command -v uv 2>/dev/null) RUN := $(if $(UV),uv run,) PYTHON := $(if $(UV),uv run python,python3) PIP := $(if $(UV),uv pip,python3 -m pip) +# Parallel workers for make test / make test-fast (override: make test PYTEST_JOBS=4) +PYTEST_JOBS ?= auto # ─── Help ──────────────────────────────────────────────────────────────────── @@ -219,8 +221,13 @@ typecheck: .PHONY: test test: - @echo "[test] pytest (not live)..." - $(RUN) pytest -m "not live" + @echo "[test] pytest (not live, -n $(PYTEST_JOBS))..." + $(RUN) pytest -n $(PYTEST_JOBS) -m "not live" + +.PHONY: test-fast +test-fast: + @echo "[test-fast] pytest (not live, not slow, -n $(PYTEST_JOBS))..." + $(RUN) pytest -n $(PYTEST_JOBS) -m "not live and not slow" .PHONY: test-live test-live: diff --git a/README.md b/README.md index 26c9e2c6..78a3a558 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

Python 3.12+ License: MIT - Version + Version PyPI Code Style: Ruff GitHub stars diff --git a/dashboard/src/components/AppVersionBadge/index.tsx b/dashboard/src/components/AppVersionBadge/index.tsx index 5e4d85cc..d9e6b821 100644 --- a/dashboard/src/components/AppVersionBadge/index.tsx +++ b/dashboard/src/components/AppVersionBadge/index.tsx @@ -3,7 +3,8 @@ import { useNavigate } from "react-router-dom"; import { Tooltip } from "antd"; import { ArrowUpCircle } from "lucide-react"; import { useUpdateStatus } from "../../hooks/useUpdateStatus"; -import { useUserRole } from "../../hooks/useUserRole"; +import { useCurrentUser } from "../../hooks/useCurrentUser"; +import { userCan } from "../../utils/permissions"; import styles from "./index.module.less"; interface AppVersionBadgeProps { @@ -13,11 +14,11 @@ interface AppVersionBadgeProps { export default function AppVersionBadge({ isMobile }: AppVersionBadgeProps) { const { t } = useTranslation(); const navigate = useNavigate(); - const role = useUserRole(); + const user = useCurrentUser(); const { status, hasUpdate } = useUpdateStatus(); - const isAdmin = role === "admin"; - if (!status?.current_version || !hasUpdate || !isAdmin) return null; + const canUpdate = userCan(user, "update"); + if (!status?.current_version || !hasUpdate || !canUpdate) return null; const tooltip = t("header.versionUpdateAvailable", { current: status.current_version, diff --git a/dashboard/src/components/AvatarDropdown.tsx b/dashboard/src/components/AvatarDropdown.tsx index 1102cbfa..145fa30d 100644 --- a/dashboard/src/components/AvatarDropdown.tsx +++ b/dashboard/src/components/AvatarDropdown.tsx @@ -43,6 +43,7 @@ import PaletteSwitcher from "./PaletteSwitcher"; import type { OctopUser } from "../api/modules/auth"; import { useLayoutMode } from "../context/LayoutModeContext"; import type { LayoutMode } from "../layouts/layoutModeStorage"; +import { userCan } from "../utils/permissions"; import styles from "./AvatarDropdown.module.less"; const GITHUB_URL = "https://github.com/TencentCloud/Octop"; @@ -259,17 +260,19 @@ export default function AvatarDropdown({ {t("account.changePassword")} - + {userCan(user, "update") && ( + + )} diff --git a/dashboard/src/components/CurrentVersionBadge/index.tsx b/dashboard/src/components/CurrentVersionBadge/index.tsx index 3dbab8a4..e6f8e59a 100644 --- a/dashboard/src/components/CurrentVersionBadge/index.tsx +++ b/dashboard/src/components/CurrentVersionBadge/index.tsx @@ -2,7 +2,8 @@ import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { Tooltip } from "antd"; import { useUpdateStatus } from "../../hooks/useUpdateStatus"; -import { useUserRole } from "../../hooks/useUserRole"; +import { useCurrentUser } from "../../hooks/useCurrentUser"; +import { userCan } from "../../utils/permissions"; import styles from "./index.module.less"; interface CurrentVersionBadgeProps { @@ -14,19 +15,19 @@ export default function CurrentVersionBadge({ }: CurrentVersionBadgeProps) { const { t } = useTranslation(); const navigate = useNavigate(); - const role = useUserRole(); + const user = useCurrentUser(); const { status } = useUpdateStatus(); const version = status?.current_version; if (!version) return null; - const isAdmin = role === "admin"; - const tooltip = isAdmin + const canUpdate = userCan(user, "update"); + const tooltip = canUpdate ? t("header.currentVersionAdmin", { version }) : t("header.currentVersion", { version }); const label = `v${version}`; - if (isAdmin) { + if (canUpdate) { return (